From 2d1d48e9303aab8e6fac4df4f2313ea015744a32 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 14 Oct 2021 17:54:36 +0800 Subject: [PATCH] media: rockchip: cif support rk3588 Signed-off-by: Zefa Chen Change-Id: I4398f9163b858c8f7dd32c88f2f2d37a9f464bcc --- drivers/media/platform/rockchip/cif/Makefile | 4 +- drivers/media/platform/rockchip/cif/capture.c | 1795 ++++++++++++++--- .../media/platform/rockchip/cif/cif-luma.c | 2 +- .../media/platform/rockchip/cif/cif-scale.c | 1171 +++++++++++ drivers/media/platform/rockchip/cif/common.c | 187 ++ drivers/media/platform/rockchip/cif/common.h | 22 + drivers/media/platform/rockchip/cif/dev.c | 643 ++++-- drivers/media/platform/rockchip/cif/dev.h | 190 +- drivers/media/platform/rockchip/cif/hw.c | 187 +- drivers/media/platform/rockchip/cif/hw.h | 15 +- .../media/platform/rockchip/cif/mipi-csi2.c | 40 +- drivers/media/platform/rockchip/cif/procfs.c | 13 +- drivers/media/platform/rockchip/cif/regs.h | 373 +++- .../media/platform/rockchip/cif/subdev-itf.c | 332 ++- .../media/platform/rockchip/cif/subdev-itf.h | 39 + include/uapi/linux/rkcif-config.h | 16 + 16 files changed, 4551 insertions(+), 478 deletions(-) create mode 100644 drivers/media/platform/rockchip/cif/cif-scale.c create mode 100644 drivers/media/platform/rockchip/cif/common.c create mode 100644 drivers/media/platform/rockchip/cif/common.h diff --git a/drivers/media/platform/rockchip/cif/Makefile b/drivers/media/platform/rockchip/cif/Makefile index fff927e78d24..279be843e093 100644 --- a/drivers/media/platform/rockchip/cif/Makefile +++ b/drivers/media/platform/rockchip/cif/Makefile @@ -6,4 +6,6 @@ video_rkcif-objs += dev.o \ cif-luma.o \ hw.o \ subdev-itf.o \ - procfs.o + procfs.o \ + cif-scale.o \ + common.o diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index a47d530cd079..b1aa346d5e36 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -22,6 +22,7 @@ #include "dev.h" #include "mipi-csi2.h" +#include "common.h" #define CIF_REQ_BUFS_MIN 3 #define CIF_MIN_WIDTH 64 @@ -33,6 +34,7 @@ #define RKCIF_PLANE_Y 0 #define RKCIF_PLANE_CBCR 1 +#define RKCIF_MAX_PLANE 3 #define STREAM_PAD_SINK 0 #define STREAM_PAD_SOURCE 1 @@ -261,6 +263,30 @@ static const struct cif_output_fmt out_fmts[] = { .raw_bpp = 16, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_Y16, .cplanes = 1, @@ -464,6 +490,12 @@ static const struct cif_input_fmt in_fmts[] = { } }; +static inline +struct rkcif_rx_buffer *to_cif_rx_buf(struct rkisp_rx_buf *dbufs) +{ + return container_of(dbufs, struct rkcif_rx_buffer, dbufs); +} + static struct v4l2_subdev *get_remote_sensor(struct rkcif_stream *stream, u16 *index) { struct media_pad *local, *remote; @@ -604,7 +636,7 @@ static int get_csi_crop_align(const struct cif_input_fmt *fmt_in) } } -static const struct +const struct cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect, u32 pad_id, int *vc) { @@ -1275,6 +1307,144 @@ static int rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream, return ret; } +static void rkcif_s_rx_buffer(struct rkcif_device *dev, struct rkisp_rx_buf *dbufs) +{ + struct media_pad *pad = media_entity_remote_pad(&dev->sditf->pads); + struct v4l2_subdev *sd; + + if (pad) + sd = media_entity_to_v4l2_subdev(pad->entity); + else + return; + + v4l2_subdev_call(sd, video, s_rx_buffer, dbufs, NULL); +} + +static void rkcif_assign_new_buffer_init_toisp(struct rkcif_stream *stream, + int channel_id) +{ + struct rkcif_device *dev = stream->cifdev; + struct rkisp_rx_buf *dbufs; + struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; + u32 frm0_addr_y; + u32 frm1_addr_y; + unsigned long flags; + + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || + mbus_cfg->type == V4L2_MBUS_CCP2) { + frm0_addr_y = get_reg_index_of_frm0_y_addr(channel_id); + frm1_addr_y = get_reg_index_of_frm1_y_addr(channel_id); + } else { + frm0_addr_y = get_dvp_reg_index_of_frm0_y_addr(channel_id); + frm1_addr_y = get_dvp_reg_index_of_frm1_y_addr(channel_id); + } + + spin_lock_irqsave(&stream->vbq_lock, flags); + + if (!stream->curr_buf_toisp) { + if (!list_empty(&stream->rx_buf_head)) { + dbufs = list_first_entry(&stream->rx_buf_head, + struct rkisp_rx_buf, + list); + if (dbufs) + list_del(&dbufs->list); + stream->curr_buf_toisp = to_cif_rx_buf(dbufs); + } + } + + if (stream->curr_buf_toisp) + rkcif_write_register(dev, frm0_addr_y, + stream->curr_buf_toisp->dummy.dma_addr); + + if (!stream->next_buf_toisp) { + if (!list_empty(&stream->rx_buf_head)) { + dbufs = list_first_entry(&stream->rx_buf_head, + struct rkisp_rx_buf, list); + if (dbufs) { + list_del(&dbufs->list); + stream->next_buf_toisp = to_cif_rx_buf(dbufs); + } else { + stream->next_buf_toisp = stream->curr_buf_toisp; + } + } else { + stream->next_buf_toisp = stream->curr_buf_toisp; + } + } + + if (stream->next_buf_toisp) + rkcif_write_register(dev, frm1_addr_y, + stream->next_buf_toisp->dummy.dma_addr); + + spin_unlock_irqrestore(&stream->vbq_lock, flags); +} + +static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream, + int channel_id) +{ + struct rkcif_device *dev = stream->cifdev; + struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; + struct rkcif_rx_buffer *buffer = NULL; + struct rkisp_rx_buf *dbufs; + u32 frm_addr_y; + int ret = 0; + unsigned long flags; + + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || + mbus_cfg->type == V4L2_MBUS_CCP2) { + frm_addr_y = stream->frame_phase & CIF_CSI_FRAME0_READY ? + get_reg_index_of_frm0_y_addr(channel_id) : + get_reg_index_of_frm1_y_addr(channel_id); + } else { + frm_addr_y = stream->frame_phase & CIF_CSI_FRAME0_READY ? + get_dvp_reg_index_of_frm0_y_addr(channel_id) : + get_dvp_reg_index_of_frm1_y_addr(channel_id); + } + spin_lock_irqsave(&stream->vbq_lock, flags); + if (!list_empty(&stream->rx_buf_head)) { + if (stream->frame_phase == CIF_CSI_FRAME0_READY) { + + dbufs = list_first_entry(&stream->rx_buf_head, + struct rkisp_rx_buf, list); + if (dbufs) { + list_del(&dbufs->list); + stream->curr_buf_toisp = to_cif_rx_buf(dbufs); + buffer = stream->curr_buf_toisp; + } + } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { + dbufs = list_first_entry(&stream->rx_buf_head, + struct rkisp_rx_buf, list); + if (dbufs) { + list_del(&dbufs->list); + stream->next_buf_toisp = to_cif_rx_buf(dbufs); + buffer = stream->next_buf_toisp; + } + } + } else { + buffer = NULL; + } + spin_unlock_irqrestore(&stream->vbq_lock, flags); + + if (buffer) { + rkcif_write_register(dev, frm_addr_y, + buffer->dummy.dma_addr); + } + return ret; +} + +static int rkcif_assign_new_buffer_pingpong_toisp(struct rkcif_stream *stream, + int init, int channel_id) +{ + int ret = 0; + + if (init) + rkcif_assign_new_buffer_init_toisp(stream, channel_id); + else + ret = rkcif_assign_new_buffer_update_toisp(stream, channel_id); + return ret; +} + static void rkcif_assign_new_buffer_init(struct rkcif_stream *stream, int channel_id) { @@ -1287,6 +1457,7 @@ static void rkcif_assign_new_buffer_init(struct rkcif_stream *stream, struct csi_channel_info *channel = &dev->channels[channel_id]; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) { frm0_addr_y = get_reg_index_of_frm0_y_addr(channel_id); frm0_addr_uv = get_reg_index_of_frm0_uv_addr(channel_id); @@ -1401,6 +1572,7 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream, unsigned long flags; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) { frm_addr_y = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_y_addr(channel_id) : @@ -1502,6 +1674,7 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream, v4l2_info(&dev->v4l2_dev, "not active buffer, skip current frame, %s stream[%d]\n", (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) ? "mipi/lvds" : "dvp", stream->id); } @@ -1569,6 +1742,7 @@ static int rkcif_update_new_buffer_wake_up_mode(struct rkcif_stream *stream) unsigned long flags; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) { frm_addr_y = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_y_addr(channel_id) : @@ -1609,6 +1783,7 @@ static int rkcif_update_new_buffer_wake_up_mode(struct rkcif_stream *stream) v4l2_info(&dev->v4l2_dev, "not active buffer, skip current frame, %s stream[%d]\n", (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) ? "mipi/lvds" : "dvp", stream->id); } @@ -1706,7 +1881,7 @@ static void rkcif_csi_set_lvds_sav_eav(struct rkcif_stream *stream, struct rkmodule_lvds_frm_sync_code *odd_sync_code = NULL; struct rkmodule_lvds_frm_sync_code *even_sync_code = NULL; - if (dev->hdr.mode == NO_HDR) { + if (dev->hdr.hdr_mode == NO_HDR) { frm_sync_code = &lvds_cfg->frm_sync_code[LVDS_CODE_GRP_LINEAR]; odd_sync_code = &frm_sync_code->odd_sync_code; even_sync_code = odd_sync_code; @@ -1714,12 +1889,12 @@ static void rkcif_csi_set_lvds_sav_eav(struct rkcif_stream *stream, if (channel->id == RKCIF_STREAM_MIPI_ID0) frm_sync_code = &lvds_cfg->frm_sync_code[LVDS_CODE_GRP_LONG]; - if (dev->hdr.mode == HDR_X2) { + if (dev->hdr.hdr_mode == HDR_X2) { if (channel->id == RKCIF_STREAM_MIPI_ID1) frm_sync_code = &lvds_cfg->frm_sync_code[LVDS_CODE_GRP_SHORT]; else frm_sync_code = &lvds_cfg->frm_sync_code[LVDS_CODE_GRP_LONG]; - } else if (dev->hdr.mode == HDR_X3) { + } else if (dev->hdr.hdr_mode == HDR_X3) { if (channel->id == RKCIF_STREAM_MIPI_ID1) frm_sync_code = &lvds_cfg->frm_sync_code[LVDS_CODE_GRP_MEDIUM]; else if (channel->id == RKCIF_STREAM_MIPI_ID2) @@ -1832,9 +2007,9 @@ static int rkcif_csi_channel_init(struct rkcif_stream *stream, channel->data_type = get_data_type(stream->cif_fmt_in->mbus_code, channel->cmd_mode_en); - if ((dev->hdr.mode == NO_HDR && stream->vc >= 0) || - (dev->hdr.mode == HDR_X2 && stream->vc > 1) || - (dev->hdr.mode == HDR_X3 && stream->vc > 2)) + if ((dev->hdr.hdr_mode == NO_HDR && stream->vc >= 0) || + (dev->hdr.hdr_mode == HDR_X2 && stream->vc > 1) || + (dev->hdr.hdr_mode == HDR_X3 && stream->vc > 2)) channel->vc = stream->vc; else channel->vc = channel->id; @@ -1956,15 +2131,235 @@ static int rkcif_csi_channel_set(struct rkcif_stream *stream, val &= ~LVDS_COMPACT; } if (stream->is_high_align) - val |= CSI_ENABLE_MIPI_HIGH_ALIGN; + val |= CSI_HIGH_ALIGN; else - val &= ~CSI_ENABLE_MIPI_HIGH_ALIGN; + val &= ~CSI_HIGH_ALIGN; rkcif_write_register(dev, get_reg_index_of_id_ctrl0(channel->id), val); return 0; } -static int rkcif_csi_stream_start(struct rkcif_stream *stream) +static int rkcif_dvp_get_input_yuv_order(struct rkcif_stream *stream) +{ + unsigned int mask; + const struct cif_input_fmt *fmt = stream->cif_fmt_in; + + switch (fmt->mbus_code) { + case MEDIA_BUS_FMT_UYVY8_2X8: + mask = CSI_YUV_INPUT_ORDER_UYVY >> 11; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + mask = CSI_YUV_INPUT_ORDER_VYUY >> 11; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + mask = CSI_YUV_INPUT_ORDER_YUYV >> 11; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + mask = CSI_YUV_INPUT_ORDER_YVYU >> 11; + break; + default: + mask = MEDIA_BUS_FMT_UYVY8_2X8; + break; + } + return mask; +} + +static int rkcif_csi_get_output_type_mask(struct rkcif_stream *stream) +{ + unsigned int mask; + const struct cif_output_fmt *fmt = stream->cif_fmt_out; + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV16: + mask = CSI_WRDDR_TYPE_YUV422SP_RK3588 | CSI_YUV_OUTPUT_ORDER_UYVY; + break; + case V4L2_PIX_FMT_NV61: + mask = CSI_WRDDR_TYPE_YUV422SP_RK3588 | CSI_YUV_OUTPUT_ORDER_VYUY; + break; + case V4L2_PIX_FMT_NV12: + mask = CSI_WRDDR_TYPE_YUV420SP_RK3588 | CSI_YUV_OUTPUT_ORDER_UYVY; + break; + case V4L2_PIX_FMT_NV21: + mask = CSI_WRDDR_TYPE_YUV420SP_RK3588 | CSI_YUV_OUTPUT_ORDER_VYUY; + break; + case V4L2_PIX_FMT_YUYV: + mask = CSI_WRDDR_TYPE_YUV_PACKET | CSI_YUV_OUTPUT_ORDER_YUYV; + break; + case V4L2_PIX_FMT_YVYU: + mask = CSI_WRDDR_TYPE_YUV_PACKET | CSI_YUV_OUTPUT_ORDER_YVYU; + break; + case V4L2_PIX_FMT_UYVY: + mask = CSI_WRDDR_TYPE_YUV_PACKET | CSI_YUV_OUTPUT_ORDER_UYVY; + break; + case V4L2_PIX_FMT_VYUY: + mask = CSI_WRDDR_TYPE_YUV_PACKET | CSI_YUV_OUTPUT_ORDER_VYUY; + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + mask = CSI_WRDDR_TYPE_RAW_COMPACT; + break; + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: + if (stream->is_compact) + mask = CSI_WRDDR_TYPE_RAW_COMPACT; + else + mask = CSI_WRDDR_TYPE_RAW_UNCOMPACT; + break; + case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_SGBRG16: + case V4L2_PIX_FMT_SGRBG16: + case V4L2_PIX_FMT_SRGGB16: + case V4L2_PIX_FMT_Y16: + mask = CSI_WRDDR_TYPE_RAW_UNCOMPACT; + break; + default: + mask = CSI_WRDDR_TYPE_RAW_COMPACT; + break; + } + return mask; +} + +/*config reg for rk3588*/ +static int rkcif_csi_channel_set_v1(struct rkcif_stream *stream, + struct csi_channel_info *channel, + enum v4l2_mbus_type mbus_type, unsigned int mode) +{ + unsigned int val = 0x0; + struct rkcif_device *dev = stream->cifdev; + struct rkcif_stream *detect_stream = &dev->stream[0]; + struct sditf_priv *priv = dev->sditf; + unsigned int wait_line = 0x3fff; + unsigned int dma_en = 0; + u8 vc = 0; + + if (channel->id >= 4) + return -EINVAL; + + if (!channel->enable) { + rkcif_write_register(dev, get_reg_index_of_id_ctrl0(channel->id), + CSI_DISABLE_CAPTURE); + return 0; + } + + rkcif_write_register_and(dev, CIF_REG_MIPI_LVDS_INTSTAT, + ~(CSI_START_INTSTAT(channel->id) | + CSI_DMA_END_INTSTAT(channel->id) | + CSI_LINE_INTSTAT_V1(channel->id))); + + /* enable id0 frame start int for sof(long frame, for hdr) */ + if (channel->id == RKCIF_STREAM_MIPI_ID0) + rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, + CSI_START_INTEN(channel->id)); + + if (priv->toisp_inf.link_mode == TOISP_NONE && detect_stream->is_line_wake_up) { + rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, + CSI_LINE_INTEN_RK3588(channel->id)); + wait_line = dev->wait_line; + } + rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1, + wait_line << 16 | wait_line); + rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3, + wait_line << 16 | wait_line); + + rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, + CSI_DMA_END_INTEN(channel->id)); + if (stream->cifdev->id_use_cnt == 0) { + val = CIF_MIPI_LVDS_SW_PRESS_VALUE_RK3588(0x3) | + CIF_MIPI_LVDS_SW_PRESS_ENABLE | + CIF_MIPI_LVDS_SW_HURRY_VALUE_RK3588(0x3) | + CIF_MIPI_LVDS_SW_HURRY_ENABLE | + CIF_MIPI_LVDS_SW_WATER_LINE_25 | + CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE; + rkcif_write_register(dev, CIF_REG_MIPI_LVDS_CTRL, val); + + rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, + CSI_ALL_ERROR_INTEN_V1); + } + rkcif_write_register(dev, get_reg_index_of_id_ctrl1(channel->id), + channel->width | (channel->height << 16)); + + rkcif_write_register(dev, get_reg_index_of_frm0_y_vlw(channel->id), + channel->virtual_width); + + if (channel->crop_en) + rkcif_write_register(dev, get_reg_index_of_id_crop_start(channel->id), + channel->crop_st_y << 16 | channel->crop_st_x); + if (stream->dma_en) + dma_en = CSI_DMA_ENABLE; + if (stream->dma_en & RKCIF_DMAEN_BY_VICAP) + rkcif_assign_new_buffer_pingpong(stream, + RKCIF_YUV_ADDR_STATE_INIT, + channel->id); + else if (stream->dma_en & RKCIF_DMAEN_BY_ISP) + rkcif_assign_new_buffer_pingpong_toisp(stream, + RKCIF_YUV_ADDR_STATE_INIT, + channel->id); + if (mbus_type == V4L2_MBUS_CSI2_DPHY || + mbus_type == V4L2_MBUS_CSI2_CPHY) { + val = CSI_ENABLE_CAPTURE | dma_en | + channel->cmd_mode_en << 26 | CSI_ENABLE_CROP_V1 | + vc << 8 | channel->data_type << 10; + + if (stream->cif_fmt_in->csi_fmt_val == CSI_WRDDR_TYPE_RGB888) + val |= CSI_WRDDR_TYPE_RAW8; + else + val |= stream->cif_fmt_in->csi_fmt_val; + val |= stream->cif_fmt_in->csi_yuv_order; + val |= rkcif_csi_get_output_type_mask(stream); + if (stream->cifdev->hdr.hdr_mode == NO_HDR) + val |= CSI_NO_HDR; + else if (stream->cifdev->hdr.hdr_mode == HDR_X2) + val |= CSI_HDR2; + else if (stream->cifdev->hdr.hdr_mode == HDR_X3) + val |= CSI_HDR3; + if (stream->cifdev->hdr.esp.mode == HDR_NORMAL_VC) + val |= CSI_HDR_MODE_VC; + else if (stream->cifdev->hdr.esp.mode == HDR_LINE_CNT) + val |= CSI_HDR_MODE_LINE_CNT; + else if (stream->cifdev->hdr.esp.mode == HDR_ID_CODE) + val |= CSI_HDR_MODE_LINE_INFO; + if (stream->cifdev->hdr.hdr_mode != NO_HDR && + stream->cifdev->hdr.esp.mode == HDR_NORMAL_VC) + val |= CSI_HDR_VC_MODE_PROTECT; + if (stream->is_high_align) + val |= CSI_HIGH_ALIGN_RK3588; + else + val &= ~CSI_HIGH_ALIGN_RK3588; + } else if (mbus_type == V4L2_MBUS_CCP2) { + //not used + rkcif_csi_set_lvds_sav_eav(stream, channel); + val = LVDS_ENABLE_CAPTURE | LVDS_MODE(channel->lvds_cfg.mode) | + LVDS_MAIN_LANE(0) | LVDS_FID(0) | + LVDS_LANES_ENABLED(dev->active_sensor->lanes); + + if (stream->is_compact) + val |= LVDS_COMPACT; + else + val &= ~LVDS_COMPACT; + } + + rkcif_write_register(dev, get_reg_index_of_id_ctrl0(channel->id), val); + rkcif_write_register(dev, CIF_REG_MIPI_EFFECT_CODE_ID0, 0x02410251); + rkcif_write_register(dev, CIF_REG_MIPI_EFFECT_CODE_ID1, 0x02420252); + stream->cifdev->id_use_cnt++; + return 0; +} + +static int rkcif_csi_stream_start(struct rkcif_stream *stream, unsigned int mode) { struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *active_sensor = dev->active_sensor; @@ -1973,16 +2368,47 @@ static int rkcif_csi_stream_start(struct rkcif_stream *stream) struct csi_channel_info *channel; u32 ret = 0; - if (stream->state != RKCIF_STATE_RESET_IN_STREAMING) + if (stream->state < RKCIF_STATE_STREAMING) stream->frame_idx = 0; - if (mbus_type == V4L2_MBUS_CSI2_DPHY) { + if (mbus_type == V4L2_MBUS_CSI2_DPHY || + mbus_type == V4L2_MBUS_CSI2_CPHY) { rkcif_csi_get_vc_num(dev, flags); channel = &dev->channels[stream->id]; channel->id = stream->id; rkcif_csi_channel_init(stream, channel); - rkcif_csi_channel_set(stream, channel, V4L2_MBUS_CSI2_DPHY); + if (stream->state != RKCIF_STATE_STREAMING) { + if ((mode & RKCIF_STREAM_MODE_CAPTURE) == RKCIF_STREAM_MODE_CAPTURE) { + stream->dma_en |= RKCIF_DMAEN_BY_VICAP; + } else if ((mode & RKCIF_STREAM_MODE_TOISP) == RKCIF_STREAM_MODE_TOISP) { + if (dev->hdr.hdr_mode == HDR_X2 && + stream->id == 0) + stream->dma_en |= RKCIF_DMAEN_BY_ISP; + else if (dev->hdr.hdr_mode == HDR_X3 && (stream->id == 0 || stream->id == 1)) + stream->dma_en |= RKCIF_DMAEN_BY_ISP; + } + if (stream->cifdev->chip_id < CHIP_RK3588_CIF) + rkcif_csi_channel_set(stream, channel, mbus_type); + else + rkcif_csi_channel_set_v1(stream, channel, mbus_type, mode); + } else { + if (stream->cifdev->chip_id > CHIP_RK3588_CIF) { + if ((mode & RKCIF_STREAM_MODE_CAPTURE) == RKCIF_STREAM_MODE_CAPTURE && + (!stream->dma_en)) { + stream->to_en_dma = RKCIF_DMAEN_BY_VICAP; + } else if ((mode & RKCIF_STREAM_MODE_TOISP) == RKCIF_STREAM_MODE_TOISP) { + if (dev->hdr.hdr_mode == HDR_X2 && + stream->id == 0 && + (!stream->dma_en)) + stream->to_en_dma = RKCIF_DMAEN_BY_ISP; + else if (dev->hdr.hdr_mode == HDR_X3 && + (stream->id == 0 || stream->id == 1) && + (!stream->dma_en)) + stream->to_en_dma = RKCIF_DMAEN_BY_ISP; + } + } + } } else if (mbus_type == V4L2_MBUS_CCP2) { rkcif_csi_get_vc_num(dev, flags); @@ -2000,14 +2426,15 @@ static int rkcif_csi_stream_start(struct rkcif_stream *stream) rkcif_csi_channel_init(stream, channel); rkcif_csi_channel_set(stream, channel, V4L2_MBUS_CCP2); } - - stream->line_int_cnt = 0; - if (stream->is_line_wake_up) - stream->is_can_stop = false; - else - stream->is_can_stop = true; - stream->state = RKCIF_STATE_STREAMING; - dev->workmode = RKCIF_WORKMODE_PINGPONG; + if (stream->state != RKCIF_STATE_STREAMING) { + stream->line_int_cnt = 0; + if (stream->is_line_wake_up) + stream->is_can_stop = false; + else + stream->is_can_stop = true; + stream->state = RKCIF_STATE_STREAMING; + dev->workmode = RKCIF_WORKMODE_PINGPONG; + } return 0; } @@ -2020,10 +2447,12 @@ static void rkcif_stream_stop(struct rkcif_stream *stream) int id; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) { id = stream->id; val = rkcif_read_register(cif_dev, get_reg_index_of_id_ctrl0(id)); - if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY) + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY) val &= ~CSI_ENABLE_CAPTURE; else val &= ~LVDS_ENABLE_CAPTURE; @@ -2040,8 +2469,17 @@ static void rkcif_stream_stop(struct rkcif_stream *stream) CSI_DMA_END_INTEN(id) | CSI_LINE_INTEN(id))); - rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, - ~CSI_ALL_ERROR_INTEN); + if (stream->cifdev->chip_id < CHIP_RK3588_CIF) { + rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, + ~CSI_ALL_ERROR_INTEN); + } else { + if (stream->cifdev->id_use_cnt == 0) { + rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, + ~CSI_ALL_ERROR_INTEN_V1); + rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_CTRL, + ~CSI_ENABLE_CAPTURE); + } + } } else { if (atomic_read(&cif_dev->pipe.stream_cnt) == 1) { @@ -2073,17 +2511,17 @@ static bool rkcif_is_extending_line_for_height(struct rkcif_device *dev, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) - dev->hdr.mode = hdr_cfg.hdr_mode; + dev->hdr = hdr_cfg; else - dev->hdr.mode = NO_HDR; + dev->hdr.hdr_mode = NO_HDR; } if (fmt && fmt->fmt_type == CIF_FMT_TYPE_RAW) { - if ((dev->hdr.mode == HDR_X2 && + if ((dev->hdr.hdr_mode == HDR_X2 && stream->id == RKCIF_STREAM_MIPI_ID1) || - (dev->hdr.mode == HDR_X3 && + (dev->hdr.hdr_mode == HDR_X3 && stream->id == RKCIF_STREAM_MIPI_ID2) || - (dev->hdr.mode == NO_HDR)) { + (dev->hdr.hdr_mode == NO_HDR)) { is_extended = true; } } @@ -2155,6 +2593,7 @@ static void rkcif_check_buffer_update_pingpong(struct rkcif_stream *stream, (!dummy_buf->vaddr)) { if (!stream->is_line_wake_up) { if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || mbus_cfg->type == V4L2_MBUS_CCP2) { frm_addr_y = stream->frame_phase_cache & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_y_addr(channel_id) : @@ -2273,12 +2712,88 @@ static void rkcif_buf_queue(struct vb2_buffer *vb) stream->id, vb->index); } +void rkcif_free_rx_buf(struct rkcif_stream *stream, int buf_num) +{ + struct rkisp_rx_buf *dbufs; + struct rkcif_rx_buffer *buf; + struct rkcif_device *dev = stream->cifdev; + int i = 0; + + if (stream->curr_buf_toisp) { + list_add_tail(&stream->curr_buf_toisp->dbufs.list, &stream->rx_buf_head); + if (stream->curr_buf_toisp == stream->next_buf_toisp) + stream->next_buf_toisp = NULL; + stream->curr_buf_toisp = NULL; + } + if (stream->next_buf_toisp) { + list_add_tail(&stream->next_buf_toisp->dbufs.list, &stream->rx_buf_head); + stream->next_buf_toisp = NULL; + } + while (!list_empty(&stream->rx_buf_head)) { + dbufs = list_first_entry(&stream->rx_buf_head, + struct rkisp_rx_buf, list); + list_del(&dbufs->list); + } + for (i = 0; i < buf_num; i++) { + buf = &stream->rx_buf[i]; + rkcif_free_common_dummy_buf(dev, &buf->dummy); + } + stream->dma_en &= ~RKCIF_DMAEN_BY_ISP; + v4l2_dbg(3, rkcif_debug, &stream->cifdev->v4l2_dev, + "free rx_buf, buf_num %d\n", buf_num); +} + +int rkcif_init_rx_buf(struct rkcif_stream *stream, int buf_num) +{ + struct rkcif_device *dev = stream->cifdev; + struct v4l2_pix_format_mplane *pixm = &stream->pixm; + struct rkcif_dummy_buffer *dummy; + struct rkcif_rx_buffer *buf; + int frm_type = 0; + int i = 0; + int ret = 0; + + if (buf_num > RKCIF_RX_BUF_MAX) + return -EINVAL; + if (dev->hdr.hdr_mode == HDR_X2 && stream->id == 0) + frm_type = BUF_MIDDLE; + else if (dev->hdr.hdr_mode == HDR_X3 && stream->id == 0) + frm_type = BUF_LONG; + else if (dev->hdr.hdr_mode == HDR_X3 && stream->id == 1) + frm_type = BUF_MIDDLE; + for (i = 0; i < buf_num; i++) { + buf = &stream->rx_buf[i]; + memset(buf, 0, sizeof(*buf)); + dummy = &buf->dummy; + dummy->size = pixm->plane_fmt[0].sizeimage; + dummy->is_need_vaddr = true; + dummy->is_need_dbuf = true; + ret = rkcif_alloc_common_dummy_buf(dev, dummy); + if (ret) + goto alloc_rx_buf_err; + buf->dbufs.is_init = false; + buf->dbufs.type = frm_type; + buf->dbufs.dbuf = dummy->dbuf; + list_add_tail(&buf->dbufs.list, &stream->rx_buf_head); + rkcif_s_rx_buffer(dev, &buf->dbufs); + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, + "init rx_buf,dma_addr 0x%llx size: 0x%x\n", + (u64)dummy->dma_addr, pixm->plane_fmt[0].sizeimage); + } + return 0; +alloc_rx_buf_err: + rkcif_free_rx_buf(stream, buf_num); + v4l2_err(&dev->v4l2_dev, "%s fail:%d\n", __func__, ret); + return ret; + +} + static int rkcif_create_dummy_buf(struct rkcif_stream *stream) { u32 fourcc; struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; - struct rkcif_hw *hw_dev = dev->hw_dev; + int ret = 0; /* get a maximum plane size */ dummy_buf->size = max3(stream->pixm.plane_fmt[0].bytesperline * @@ -2296,10 +2811,10 @@ static int rkcif_create_dummy_buf(struct rkcif_stream *stream) fourcc == V4L2_PIX_FMT_UYVY || fourcc == V4L2_PIX_FMT_VYUY) dummy_buf->size *= 2; - dummy_buf->vaddr = dma_alloc_coherent(hw_dev->dev, dummy_buf->size, - &dummy_buf->dma_addr, - GFP_KERNEL); - if (!dummy_buf->vaddr) { + dummy_buf->is_need_vaddr = true; + dummy_buf->is_need_dbuf = true; + ret = rkcif_alloc_common_dummy_buf(dev, dummy_buf); + if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to allocate the memory for dummy buffer\n"); return -ENOMEM; @@ -2308,18 +2823,16 @@ static int rkcif_create_dummy_buf(struct rkcif_stream *stream) v4l2_info(&dev->v4l2_dev, "Allocate dummy buffer, size: 0x%08x\n", dummy_buf->size); - return 0; + return ret; } static void rkcif_destroy_dummy_buf(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; - struct rkcif_hw *hw_dev = dev->hw_dev; if (dummy_buf->vaddr) - dma_free_coherent(hw_dev->dev, dummy_buf->size, - dummy_buf->vaddr, dummy_buf->dma_addr); + rkcif_free_common_dummy_buf(dev, dummy_buf); dummy_buf->dma_addr = 0; dummy_buf->vaddr = NULL; } @@ -2333,7 +2846,7 @@ static void rkcif_do_cru_reset(struct rkcif_device *dev) if (dev->luma_vdev.enable) rkcif_stop_luma(&dev->luma_vdev); - if (dev->hdr.mode != NO_HDR) { + if (dev->hdr.hdr_mode != NO_HDR) { if (dev->chip_id == CHIP_RK1808_CIF) { val = rkcif_read_register(dev, CIF_REG_MIPI_WATER_LINE); val |= CIF_MIPI_LVDS_SW_DMA_IDLE_RK1808; @@ -2367,6 +2880,19 @@ static void rkcif_do_cru_reset(struct rkcif_device *dev) } } +static void rkcif_do_soft_reset(struct rkcif_device *dev) +{ + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) + rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_CTRL, 0x000A0000); + else + rkcif_write_register_or(dev, CIF_REG_DVP_CTRL, 0x000A0000); + usleep_range(10, 20); + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, + "vicap do soft reset 0x%x\n", 0x000A0000); +} + static void rkcif_release_rdbk_buf(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; @@ -2409,9 +2935,9 @@ static void rkcif_release_rdbk_buf(struct rkcif_stream *stream) } -static void rkcif_stop_streaming(struct vb2_queue *queue) +void rkcif_do_stop_stream(struct rkcif_stream *stream, + unsigned int mode) { - struct rkcif_stream *stream = queue->drv_priv; struct rkcif_vdev_node *node = &stream->vnode; struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; @@ -2423,81 +2949,104 @@ static void rkcif_stop_streaming(struct vb2_queue *queue) mutex_lock(&dev->stream_lock); - v4l2_info(&dev->v4l2_dev, "stream[%d] start stopping\n", stream->id); + v4l2_info(&dev->v4l2_dev, "stream[%d] start stopping, total mode 0x%x, cur 0x%x\n", + stream->id, stream->cur_stream_mode, mode); - stream->stopping = true; + if (mode == stream->cur_stream_mode) { + stream->stopping = true; - ret = wait_event_timeout(stream->wq_stopped, - stream->state != RKCIF_STATE_STREAMING, - msecs_to_jiffies(1000)); - if (!ret) { - rkcif_stream_stop(stream); - stream->stopping = false; - } - - media_pipeline_stop(&node->vdev.entity); - ret = dev->pipe.set_stream(&dev->pipe, false); - if (ret < 0) - v4l2_err(v4l2_dev, "pipeline stream-off failed error:%d\n", - ret); - - dev->is_start_hdr = false; - stream->is_dvp_yuv_addr_init = false; - - /* release buffers */ - if (stream->curr_buf) - list_add_tail(&stream->curr_buf->queue, &stream->buf_head); - - if (stream->next_buf && - stream->next_buf != stream->curr_buf) - list_add_tail(&stream->next_buf->queue, &stream->buf_head); - - if (dev->hdr.mode != NO_HDR) - rkcif_release_rdbk_buf(stream); - - stream->curr_buf = NULL; - stream->next_buf = NULL; - - while (!list_empty(&stream->buf_head)) { - buf = list_first_entry(&stream->buf_head, - struct rkcif_buffer, queue); - list_del(&buf->queue); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - ret = dev->pipe.close(&dev->pipe); - if (ret < 0) - v4l2_err(v4l2_dev, "pipeline close failed error:%d\n", ret); - - if (dev->hdr.mode == HDR_X2) { - if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_READY && - dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_READY) { - dev->can_be_reset = true; + ret = wait_event_timeout(stream->wq_stopped, + stream->state != RKCIF_STATE_STREAMING, + msecs_to_jiffies(1000)); + if (!ret) { + rkcif_stream_stop(stream); + stream->stopping = false; } - } else if (dev->hdr.mode == HDR_X3) { - if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_READY && - dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_READY && - dev->stream[RKCIF_STREAM_MIPI_ID2].state == RKCIF_STATE_READY) { - dev->can_be_reset = true; + + media_pipeline_stop(&node->vdev.entity); + ret = dev->pipe.set_stream(&dev->pipe, false); + if (ret < 0) + v4l2_err(v4l2_dev, "pipeline stream-off failed error:%d\n", + ret); + + dev->is_start_hdr = false; + stream->is_dvp_yuv_addr_init = false; + } else if (mode == RKCIF_STREAM_MODE_CAPTURE) { + //only stop dma + stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP; + wait_event_timeout(stream->wq_stopped, + stream->to_stop_dma != RKCIF_DMAEN_BY_VICAP, + msecs_to_jiffies(1000)); + } else if (mode == RKCIF_STREAM_MODE_TOISP) { + //only stop dma + stream->to_stop_dma = RKCIF_DMAEN_BY_ISP; + wait_event_timeout(stream->wq_stopped, + stream->to_stop_dma != RKCIF_DMAEN_BY_ISP, + msecs_to_jiffies(1000)); + } + if ((mode & RKCIF_STREAM_MODE_CAPTURE) == RKCIF_STREAM_MODE_CAPTURE) { + /* release buffers */ + if (stream->curr_buf) + list_add_tail(&stream->curr_buf->queue, &stream->buf_head); + + if (stream->next_buf && + stream->next_buf != stream->curr_buf) + list_add_tail(&stream->next_buf->queue, &stream->buf_head); + + if (dev->hdr.hdr_mode != NO_HDR) + rkcif_release_rdbk_buf(stream); + + stream->curr_buf = NULL; + stream->next_buf = NULL; + + while (!list_empty(&stream->buf_head)) { + buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - } else { - dev->can_be_reset = true; + stream->dma_en &= ~RKCIF_DMAEN_BY_VICAP; } - for (i = 0; i < hw_dev->dev_num; i++) { - if (atomic_read(&hw_dev->cif_dev[i]->pipe.stream_cnt) != 0) { - can_reset = false; - break; + if (mode == stream->cur_stream_mode) { + ret = dev->pipe.close(&dev->pipe); + if (ret < 0) { + v4l2_err(v4l2_dev, "pipeline close failed error:%d\n", ret); + pm_runtime_put_sync(dev->dev); + } + + stream->cur_stream_mode &= ~mode; + if (stream->cur_stream_mode == 0) { + if (dev->hdr.hdr_mode == HDR_X2) { + if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_READY && + dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_READY) { + dev->can_be_reset = true; + } + } else if (dev->hdr.hdr_mode == HDR_X3) { + if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_READY && + dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_READY && + dev->stream[RKCIF_STREAM_MIPI_ID2].state == RKCIF_STATE_READY) { + dev->can_be_reset = true; + } + } else { + dev->can_be_reset = true; + } + + for (i = 0; i < hw_dev->dev_num; i++) { + if (atomic_read(&hw_dev->cif_dev[i]->pipe.stream_cnt) != 0) { + can_reset = false; + break; + } + } + if (dev->can_be_reset && dev->chip_id >= CHIP_RK3588_CIF) + rkcif_do_soft_reset(dev); + if (dev->can_be_reset && can_reset) { + rkcif_do_cru_reset(dev); + dev->can_be_reset = false; + dev->reset_work_cancel = true; + } } } - - if (dev->can_be_reset && can_reset) { - rkcif_do_cru_reset(dev); - dev->can_be_reset = false; - dev->reset_work_cancel = true; - } - pm_runtime_put_sync(dev->dev); - if (!atomic_read(&dev->pipe.stream_cnt) && dev->dummy_buf.vaddr) rkcif_destroy_dummy_buf(stream); @@ -2506,6 +3055,13 @@ static void rkcif_stop_streaming(struct vb2_queue *queue) mutex_unlock(&dev->stream_lock); } +static void rkcif_stop_streaming(struct vb2_queue *queue) +{ + struct rkcif_stream *stream = queue->drv_priv; + + rkcif_do_stop_stream(stream, RKCIF_STREAM_MODE_CAPTURE); +} + /* * CIF supports the following input modes, * YUV, the default mode @@ -2579,6 +3135,71 @@ static u32 rkcif_determine_input_mode(struct rkcif_stream *stream) return mode; } +static u32 rkcif_determine_input_mode_rk3588(struct rkcif_stream *stream) +{ + struct rkcif_device *dev = stream->cifdev; + struct rkcif_sensor_info *sensor_info = dev->active_sensor; + struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; + __u32 intf = BT656_STD_RAW; + u32 mode = INPUT_MODE_YUV; + v4l2_std_id std; + int ret; + + ret = v4l2_subdev_call(sensor_info->sd, video, querystd, &std); + if (ret == 0) { + /* retrieve std from sensor if exist */ + switch (std) { + case V4L2_STD_NTSC: + case V4L2_STD_PAL: + mode = INPUT_BT656_YUV422 | TRANSMIT_INTERFACE_RK3588; + break; + case V4L2_STD_ATSC: + mode = INPUT_BT1120_YUV422 | TRANSMIT_INTERFACE_RK3588; + break; + default: + v4l2_err(&dev->v4l2_dev, + "std: %lld is not supported", std); + } + mode |= CSI_WRDDR_TYPE_RAW8 << 6; + } else { + /* determine input mode by mbus_code (fmt_type) */ + switch (stream->cif_fmt_in->fmt_type) { + case CIF_FMT_TYPE_YUV: + if (sensor_info->mbus.type == V4L2_MBUS_BT656) { + if ((sensor_info->mbus.flags & CIF_DVP_PCLK_DUAL_EDGE) == CIF_DVP_PCLK_DUAL_EDGE) + mode = INPUT_BT1120_YUV422; + else + mode = INPUT_BT656_YUV422; + } else { + mode = INPUT_BT601_YUV422; + } + mode |= CSI_WRDDR_TYPE_RAW8 << 6; + break; + case CIF_FMT_TYPE_RAW: + ret = v4l2_subdev_call(terminal_sensor->sd, + core, ioctl, + RKMODULE_GET_BT656_INTF_TYPE, + &intf); + if (!ret) { + if (intf == BT656_SONY_RAW) + mode = INPUT_SONY_RAW; + else + mode = INPUT_BT601_RAW; + } else { + mode = INPUT_BT601_RAW; + } + mode |= stream->cif_fmt_in->csi_fmt_val << 6; + break; + } + if (stream->cif_fmt_in->field == V4L2_FIELD_NONE) + mode |= TRANSMIT_PROGRESS_RK3588; + else + mode |= TRANSMIT_INTERFACE_RK3588; + } + + return mode; +} + static inline u32 rkcif_scl_ctl(struct rkcif_stream *stream) { u32 fmt_type = stream->cif_fmt_in->fmt_type; @@ -2627,6 +3248,9 @@ static u32 rkcif_align_bits_per_pixel(struct rkcif_stream *stream, case V4L2_PIX_FMT_SGBRG12: case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_SGBRG16: + case V4L2_PIX_FMT_SGRBG16: + case V4L2_PIX_FMT_SRGGB16: case V4l2_PIX_FMT_SPD16: case V4l2_PIX_FMT_EBD8: if (stream->cifdev->chip_id < CHIP_RV1126_CIF) { @@ -2644,7 +3268,8 @@ static u32 rkcif_align_bits_per_pixel(struct rkcif_stream *stream, } break; default: - pr_err("fourcc: %d is not supported!\n", fmt->fourcc); + v4l2_err(&stream->cifdev->v4l2_dev, "fourcc: %d is not supported!\n", + fmt->fourcc); break; } } @@ -2678,7 +3303,8 @@ static void rkcif_sync_crop_info(struct rkcif_stream *stream) if (dev->terminal_sensor.sd) { input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; - + input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + input_sel.pad = 0; ret = v4l2_subdev_call(dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); @@ -2750,7 +3376,9 @@ static int rkcif_sanity_check_fmt(struct rkcif_stream *stream, return -EINVAL; } - if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) { + if (dev->active_sensor && + (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY)) { if (crop->left > 0) { int align_x = get_csi_crop_align(stream->cif_fmt_in); @@ -2761,7 +3389,7 @@ static int rkcif_sanity_check_fmt(struct rkcif_stream *stream, return -EINVAL; } } - } else if (dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) { + } else if (dev->active_sensor && dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) { if (crop->left % 4 != 0 && crop->width % 4 != 0) { v4l2_err(v4l2_dev, "ERROR: lvds crop left and width must align %d\n", 4); @@ -2817,6 +3445,7 @@ int rkcif_update_sensor_info(struct rkcif_stream *stream) } if (terminal_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + terminal_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY || terminal_sensor->mbus.type == V4L2_MBUS_CCP2) { switch (terminal_sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) { case V4L2_MBUS_CSI2_1_LANE: @@ -2843,7 +3472,84 @@ int rkcif_update_sensor_info(struct rkcif_stream *stream) return ret; } -static int rkcif_stream_start(struct rkcif_stream *stream) +static int rkcif_dvp_get_output_type_mask(struct rkcif_stream *stream) +{ + unsigned int mask; + const struct cif_output_fmt *fmt = stream->cif_fmt_out; + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV16: + mask = (CSI_WRDDR_TYPE_YUV422SP_RK3588 << 11) | + (CSI_YUV_OUTPUT_ORDER_UYVY << 1); + break; + case V4L2_PIX_FMT_NV61: + mask = (CSI_WRDDR_TYPE_YUV422SP_RK3588 << 11) | + (CSI_YUV_OUTPUT_ORDER_VYUY << 1); + break; + case V4L2_PIX_FMT_NV12: + mask = (CSI_WRDDR_TYPE_YUV420SP_RK3588 << 11) | + (CSI_YUV_OUTPUT_ORDER_UYVY << 1); + break; + case V4L2_PIX_FMT_NV21: + mask = (CSI_WRDDR_TYPE_YUV420SP_RK3588 << 11) | + (CSI_YUV_OUTPUT_ORDER_VYUY << 1); + break; + case V4L2_PIX_FMT_YUYV: + mask = (CSI_WRDDR_TYPE_YUV_PACKET << 11) | + (CSI_YUV_OUTPUT_ORDER_YUYV << 1); + break; + case V4L2_PIX_FMT_YVYU: + mask = (CSI_WRDDR_TYPE_YUV_PACKET << 11) | + (CSI_YUV_OUTPUT_ORDER_YVYU << 1); + break; + case V4L2_PIX_FMT_UYVY: + mask = (CSI_WRDDR_TYPE_YUV_PACKET << 11) | + (CSI_YUV_OUTPUT_ORDER_UYVY << 1); + break; + case V4L2_PIX_FMT_VYUY: + mask = (CSI_WRDDR_TYPE_YUV_PACKET << 11) | + (CSI_YUV_OUTPUT_ORDER_VYUY << 1); + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + mask = CSI_WRDDR_TYPE_RAW_COMPACT << 11; + break; + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: + if (stream->is_compact) + mask = CSI_WRDDR_TYPE_RAW_COMPACT << 11; + else + mask = CSI_WRDDR_TYPE_RAW_UNCOMPACT << 11; + break; + case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_SGBRG16: + case V4L2_PIX_FMT_SGRBG16: + case V4L2_PIX_FMT_SRGGB16: + case V4L2_PIX_FMT_Y16: + mask = CSI_WRDDR_TYPE_RAW_UNCOMPACT << 11; + break; + default: + mask = CSI_WRDDR_TYPE_RAW_COMPACT << 11; + break; + } + return mask; +} + +static int rkcif_stream_start(struct rkcif_stream *stream, unsigned int mode) { u32 val, mbus_flags, href_pol, vsync_pol, xfer_mode = 0, yc_swap = 0, inputmode = 0, @@ -2852,20 +3558,28 @@ static int rkcif_stream_start(struct rkcif_stream *stream) multi_id_mode = BT656_1120_MULTI_ID_MODE_1, multi_id_sel = BT656_1120_MULTI_ID_SEL_LSB, bt1120_edge_mode = BT1120_CLOCK_SINGLE_EDGES, - bt1120_flags = 0; + bt1120_flags = 0, + out_fmt_mask = 0, + in_fmt_yuv_order = 0; struct rkmodule_bt656_mbus_info bt1120_info; struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *sensor_info; struct v4l2_mbus_config *mbus; struct rkcif_dvp_sof_subdev *sof_sd = &dev->dvp_sof_subdev; const struct cif_output_fmt *fmt; + unsigned int dma_en = 0; - if (stream->state != RKCIF_STATE_RESET_IN_STREAMING) + if (stream->state < RKCIF_STATE_STREAMING) stream->frame_idx = 0; sensor_info = dev->active_sensor; mbus = &sensor_info->mbus; + if ((mode & RKCIF_STREAM_MODE_CAPTURE) == RKCIF_STREAM_MODE_CAPTURE) { + dma_en = DVP_DMA_EN; + stream->dma_en = RKCIF_DMAEN_BY_VICAP; + } + if (sensor_info->sd && mbus->type == V4L2_MBUS_BT656) { int ret; @@ -2897,10 +3611,12 @@ static int rkcif_stream_start(struct rkcif_stream *stream) mbus_flags = mbus->flags; if ((mbus_flags & CIF_DVP_PCLK_DUAL_EDGE) == CIF_DVP_PCLK_DUAL_EDGE) { - bt1120_edge_mode = BT1120_CLOCK_DOUBLE_EDGES; + bt1120_edge_mode = (dev->chip_id < CHIP_RK3588_CIF ? + BT1120_CLOCK_DOUBLE_EDGES : BT1120_CLOCK_DOUBLE_EDGES_RK3588); rkcif_enable_dvp_clk_dual_edge(dev, true); } else { - bt1120_edge_mode = BT1120_CLOCK_SINGLE_EDGES; + bt1120_edge_mode = dev->chip_id < CHIP_RK3588_CIF ? + BT1120_CLOCK_SINGLE_EDGES : BT1120_CLOCK_SINGLE_EDGES_RK3588; rkcif_enable_dvp_clk_dual_edge(dev, false); } @@ -2914,7 +3630,10 @@ static int rkcif_stream_start(struct rkcif_stream *stream) vsync_pol = (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ? VSY_HIGH_ACTIVE : VSY_LOW_ACTIVE; - inputmode = rkcif_determine_input_mode(stream); + if (dev->chip_id < CHIP_RK3588_CIF) + inputmode = rkcif_determine_input_mode(stream); + else + inputmode = rkcif_determine_input_mode_rk3588(stream); if (dev->chip_id <= CHIP_RK1808_CIF) { if (inputmode == INPUT_MODE_BT1120) { if (stream->cif_fmt_in->field == V4L2_FIELD_NONE) @@ -2924,7 +3643,7 @@ static int rkcif_stream_start(struct rkcif_stream *stream) if (CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->dvp_fmt_val)) yc_swap = BT1120_YC_SWAP; } - } else { + } else if (dev->chip_id < CHIP_RK3588_CIF) { if (sensor_info->mbus.type == V4L2_MBUS_BT656) { if (stream->cif_fmt_in->field == V4L2_FIELD_NONE) xfer_mode = BT1120_TRANSMIT_PROGRESS; @@ -2936,9 +3655,14 @@ static int rkcif_stream_start(struct rkcif_stream *stream) if (CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->dvp_fmt_val)) yc_swap = BT1120_YC_SWAP; } + } else { + if ((inputmode & INPUT_BT1120_YUV422) == INPUT_BT1120_YUV422) + if (CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->dvp_fmt_val)) + yc_swap = BT1120_YC_SWAP_RK3588; } - if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) { + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY) { inputmode = INPUT_MODE_MIPI; /* if cif is linked with mipi, @@ -2956,16 +3680,30 @@ static int rkcif_stream_start(struct rkcif_stream *stream) mipimode = MIPI_MODE_32BITS_BYPASS; } - val = vsync_pol | href_pol | inputmode | mipimode - | stream->cif_fmt_out->fmt_val - | stream->cif_fmt_in->dvp_fmt_val - | xfer_mode | yc_swap | multi_id_en - | multi_id_sel | multi_id_mode | bt1120_edge_mode; - - if (stream->is_high_align) - val |= CIF_HIGH_ALIGN; - else - val &= ~CIF_HIGH_ALIGN; + if (dev->chip_id < CHIP_RK3588_CIF) { + val = vsync_pol | href_pol | inputmode | mipimode + | stream->cif_fmt_out->fmt_val + | stream->cif_fmt_in->dvp_fmt_val + | xfer_mode | yc_swap | multi_id_en + | multi_id_sel | multi_id_mode | bt1120_edge_mode; + if (stream->is_high_align) + val |= CIF_HIGH_ALIGN; + else + val &= ~CIF_HIGH_ALIGN; + } else { + out_fmt_mask = rkcif_dvp_get_output_type_mask(stream); + in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order(stream); + val = vsync_pol | href_pol | inputmode + | yc_swap + | out_fmt_mask + | in_fmt_yuv_order + | multi_id_en + | multi_id_sel | multi_id_mode | bt1120_edge_mode; + if (stream->is_high_align) + val |= CIF_HIGH_ALIGN_RK3588; + else + val &= ~CIF_HIGH_ALIGN_RK3588; + } rkcif_write_register(dev, CIF_REG_DVP_FOR, val); val = stream->pixm.width; @@ -3002,17 +3740,29 @@ static int rkcif_stream_start(struct rkcif_stream *stream) if (atomic_read(&dev->pipe.stream_cnt) <= 1) rkcif_write_register(dev, CIF_REG_DVP_FRAME_STATUS, FRAME_STAT_CLS); - rkcif_write_register(dev, CIF_REG_DVP_INTSTAT, INTSTAT_CLS); - rkcif_write_register(dev, CIF_REG_DVP_SCL_CTRL, rkcif_scl_ctl(stream)); - - if (dev->chip_id < CHIP_RK1808_CIF) - rkcif_assign_new_buffer_oneframe(stream, - RKCIF_YUV_ADDR_STATE_INIT); - else - rkcif_assign_new_buffer_pingpong(stream, - RKCIF_YUV_ADDR_STATE_INIT, - stream->id); + if (dev->chip_id < CHIP_RK3588_CIF) { + rkcif_write_register(dev, CIF_REG_DVP_INTSTAT, INTSTAT_CLS); + rkcif_write_register(dev, CIF_REG_DVP_SCL_CTRL, rkcif_scl_ctl(stream)); + rkcif_write_register_or(dev, CIF_REG_DVP_INTEN, + DVP_DMA_END_INTSTAT(stream->id) | + INTSTAT_ERR | PST_INF_FRAME_END); + /* enable line int for sof */ + rkcif_write_register(dev, CIF_REG_DVP_LINE_INT_NUM, 0x1); + rkcif_write_register_or(dev, CIF_REG_DVP_INTEN, LINE_INT_EN); + } else { + rkcif_write_register(dev, CIF_REG_DVP_INTSTAT, 0x3c3ffff); + rkcif_write_register_or(dev, CIF_REG_DVP_INTEN, 0x033ffff);//0x3c3ffff + } + if (dma_en) { + if (dev->chip_id < CHIP_RK1808_CIF) + rkcif_assign_new_buffer_oneframe(stream, + RKCIF_YUV_ADDR_STATE_INIT); + else + rkcif_assign_new_buffer_pingpong(stream, + RKCIF_YUV_ADDR_STATE_INIT, + stream->id); + } rkcif_write_register_or(dev, CIF_REG_DVP_INTEN, DVP_DMA_END_INTSTAT(stream->id) | INTSTAT_ERR | PST_INF_FRAME_END); @@ -3028,13 +3778,24 @@ static int rkcif_stream_start(struct rkcif_stream *stream) else workmode = MODE_LINELOOP; - if (inputmode == INPUT_MODE_BT1120) { + if ((inputmode & INPUT_MODE_BT1120) == INPUT_MODE_BT1120) { workmode = MODE_PINGPONG; dev->workmode = RKCIF_WORKMODE_PINGPONG; } - rkcif_write_register(dev, CIF_REG_DVP_CTRL, + if (dev->chip_id < CHIP_RK3588_CIF) + rkcif_write_register(dev, CIF_REG_DVP_CTRL, AXI_BURST_16 | workmode | ENABLE_CAPTURE); + else + rkcif_write_register(dev, CIF_REG_DVP_CTRL, + DVP_SW_WATER_LINE_25 + | dma_en + | DVP_PRESS_EN + | DVP_HURRY_EN + | DVP_SW_WATER_LINE_25 + | DVP_SW_PRESS_VALUE(3) + | DVP_SW_HURRY_VALUE(3) + | ENABLE_CAPTURE); atomic_set(&sof_sd->frm_sync_seq, 0); stream->state = RKCIF_STATE_STREAMING; @@ -3043,9 +3804,8 @@ static int rkcif_stream_start(struct rkcif_stream *stream) return 0; } -static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) +int rkcif_do_start_stream(struct rkcif_stream *stream, unsigned int mode) { - struct rkcif_stream *stream = queue->drv_priv; struct rkcif_vdev_node *node = &stream->vnode; struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; @@ -3058,17 +3818,17 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) v4l2_info(&dev->v4l2_dev, "stream[%d] start streaming\n", stream->id); mutex_lock(&dev->stream_lock); - - if (WARN_ON(stream->state != RKCIF_STATE_READY)) { + if ((stream->cur_stream_mode & RKCIF_STREAM_MODE_CAPTURE) == mode) { ret = -EBUSY; v4l2_err(v4l2_dev, "stream in busy state\n"); goto destroy_buf; } + if (stream->dma_en == 0) + stream->fs_cnt_in_single_frame = 0; if (stream->is_line_wake_up) stream->is_line_inten = true; else stream->is_line_inten = false; - stream->fs_cnt_in_single_frame = 0; if (dev->active_sensor) { ret = rkcif_update_sensor_info(stream); @@ -3086,9 +3846,9 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) - dev->hdr.mode = hdr_cfg.hdr_mode; + dev->hdr = hdr_cfg; else - dev->hdr.mode = NO_HDR; + dev->hdr.hdr_mode = NO_HDR; ret = v4l2_subdev_call(terminal_sensor->sd, video, g_frame_interval, &terminal_sensor->fi); @@ -3119,83 +3879,87 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) } } - /* enable clocks/power-domains */ - ret = pm_runtime_get_sync(dev->dev); - if (ret < 0) { - v4l2_err(v4l2_dev, "Failed to get runtime pm, %d\n", - ret); - goto destroy_buf; - } + if (stream->cur_stream_mode == RKCIF_STREAM_MODE_NONE) { + /* enable clocks/power-domains */ + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to get runtime pm, %d\n", + ret); + goto destroy_buf; + } - ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true); - if (ret < 0) { - v4l2_err(v4l2_dev, "open cif pipeline failed %d\n", - ret); - goto destroy_buf; - } + ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true); + if (ret < 0) { + v4l2_err(v4l2_dev, "open cif pipeline failed %d\n", + ret); + goto destroy_buf; + } - /* - * start sub-devices - * When use bt601, the sampling edge of cif is random, - * can be rising or fallling after powering on cif. - * To keep the coherence of edge, open sensor in advance. - */ - if (sensor_info->mbus.type == V4L2_MBUS_PARALLEL || - rkmodule_stream_seq == RKMODULE_START_STREAM_FRONT) { - ret = dev->pipe.set_stream(&dev->pipe, true); - if (ret < 0) - goto runtime_put; + /* + * start sub-devices + * When use bt601, the sampling edge of cif is random, + * can be rising or fallling after powering on cif. + * To keep the coherence of edge, open sensor in advance. + */ + if (sensor_info->mbus.type == V4L2_MBUS_PARALLEL || + rkmodule_stream_seq == RKMODULE_START_STREAM_FRONT) { + ret = dev->pipe.set_stream(&dev->pipe, true); + if (ret < 0) + goto runtime_put; + } } - if (dev->chip_id >= CHIP_RK1808_CIF) { - if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || - dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) - ret = rkcif_csi_stream_start(stream); + if (dev->active_sensor && + (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CCP2)) + ret = rkcif_csi_stream_start(stream, mode); else - ret = rkcif_stream_start(stream); + ret = rkcif_stream_start(stream, mode); } else { - ret = rkcif_stream_start(stream); + ret = rkcif_stream_start(stream, mode); } if (ret < 0) goto runtime_put; - ret = media_pipeline_start(&node->vdev.entity, &dev->pipe.pipe); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "start pipeline failed %d\n", - ret); - goto pipe_stream_off; - } + if (stream->cur_stream_mode == RKCIF_STREAM_MODE_NONE) { + ret = media_pipeline_start(&node->vdev.entity, &dev->pipe.pipe); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "start pipeline failed %d\n", + ret); + goto pipe_stream_off; + } - if (sensor_info->mbus.type != V4L2_MBUS_PARALLEL && - rkmodule_stream_seq != RKMODULE_START_STREAM_FRONT) { - ret = dev->pipe.set_stream(&dev->pipe, true); - if (ret < 0) - goto stop_stream; + if (sensor_info->mbus.type != V4L2_MBUS_PARALLEL && + rkmodule_stream_seq != RKMODULE_START_STREAM_FRONT) { + ret = dev->pipe.set_stream(&dev->pipe, true); + if (ret < 0) + goto stop_stream; + } } - - if (dev->hdr.mode == NO_HDR) { - if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING) { - rkcif_start_luma(&dev->luma_vdev, + if (dev->chip_id == CHIP_RV1126_CIF || + dev->chip_id == CHIP_RV1126_CIF_LITE || + dev->chip_id == CHIP_RK3568_CIF) { + if (dev->hdr.hdr_mode == NO_HDR) { + if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING) + rkcif_start_luma(&dev->luma_vdev, + dev->stream[RKCIF_STREAM_MIPI_ID0].cif_fmt_in); + } else if (dev->hdr.hdr_mode == HDR_X2) { + if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING && + dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_STREAMING) + rkcif_start_luma(&dev->luma_vdev, + dev->stream[RKCIF_STREAM_MIPI_ID0].cif_fmt_in); + } else if (dev->hdr.hdr_mode == HDR_X3) { + if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING && + dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_STREAMING && + dev->stream[RKCIF_STREAM_MIPI_ID2].state == RKCIF_STATE_STREAMING) + rkcif_start_luma(&dev->luma_vdev, dev->stream[RKCIF_STREAM_MIPI_ID0].cif_fmt_in); } - } else if (dev->hdr.mode == HDR_X2) { - if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING && - dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_STREAMING) { - rkcif_start_luma(&dev->luma_vdev, - dev->stream[RKCIF_STREAM_MIPI_ID0].cif_fmt_in); - } - } else if (dev->hdr.mode == HDR_X3) { - if (dev->stream[RKCIF_STREAM_MIPI_ID0].state == RKCIF_STATE_STREAMING && - dev->stream[RKCIF_STREAM_MIPI_ID1].state == RKCIF_STATE_STREAMING && - dev->stream[RKCIF_STREAM_MIPI_ID2].state == RKCIF_STATE_STREAMING) { - rkcif_start_luma(&dev->luma_vdev, - dev->stream[RKCIF_STREAM_MIPI_ID0].cif_fmt_in); - } } - dev->reset_work_cancel = false; - + stream->cur_stream_mode |= mode; goto out; stop_stream: @@ -3225,6 +3989,15 @@ out: return ret; } +static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkcif_stream *stream = queue->drv_priv; + int ret = 0; + + ret = rkcif_do_start_stream(stream, RKCIF_STREAM_MODE_CAPTURE); + return ret; +} + static struct vb2_ops rkcif_vb2_ops = { .queue_setup = rkcif_queue_setup, .buf_queue = rkcif_buf_queue, @@ -3262,7 +4035,7 @@ static int rkcif_init_vb2_queue(struct vb2_queue *q, return vb2_queue_init(q); } -static void rkcif_set_fmt(struct rkcif_stream *stream, +void rkcif_set_fmt(struct rkcif_stream *stream, struct v4l2_pix_format_mplane *pixm, bool try) { @@ -3276,6 +4049,9 @@ static void rkcif_set_fmt(struct rkcif_stream *stream, struct rkcif_extend_info *extend_line = &stream->extend_line; int ret, vc; + for (i = 0; i < RKCIF_MAX_PLANE; i++) + memset(&pixm->plane_fmt[i], 0, sizeof(struct v4l2_plane_pix_format)); + fmt = find_output_fmt(stream, pixm->pixelformat); if (!fmt) fmt = &out_fmts[0]; @@ -3295,9 +4071,9 @@ static void rkcif_set_fmt(struct rkcif_stream *stream, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) - dev->hdr.mode = hdr_cfg.hdr_mode; + dev->hdr = hdr_cfg; else - dev->hdr.mode = NO_HDR; + dev->hdr.hdr_mode = NO_HDR; dev->terminal_sensor.raw_rect = input_rect; } @@ -3355,6 +4131,7 @@ static void rkcif_set_fmt(struct rkcif_stream *stream, if (fmt->fmt_type == CIF_FMT_TYPE_RAW && stream->is_compact && (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY || dev->active_sensor->mbus.type == V4L2_MBUS_CCP2)) { bpl = ALIGN(width * fmt->raw_bpp / 8, 256); } else { @@ -3413,6 +4190,7 @@ void rkcif_stream_init(struct rkcif_device *dev, u32 id) stream->cifdev = dev; INIT_LIST_HEAD(&stream->buf_head); + INIT_LIST_HEAD(&stream->rx_buf_head); spin_lock_init(&stream->vbq_lock); spin_lock_init(&stream->fps_lock); stream->state = RKCIF_STATE_READY; @@ -3467,6 +4245,10 @@ void rkcif_stream_init(struct rkcif_device *dev, u32 id) dev->wait_line_cache = 0; dev->wait_line_bak = 0; } + stream->cur_stream_mode = 0; + stream->dma_en = 0; + stream->to_en_dma = 0; + stream->to_stop_dma = 0; } @@ -3500,7 +4282,8 @@ static int rkcif_fh_open(struct file *filp) if (cifdev->chip_id == CHIP_RK1808_CIF || cifdev->chip_id == CHIP_RV1126_CIF || cifdev->chip_id == CHIP_RV1126_CIF_LITE || - cifdev->chip_id == CHIP_RK3568_CIF) { + cifdev->chip_id == CHIP_RK3568_CIF || + cifdev->chip_id == CHIP_RK3588_CIF) { mutex_lock(&cifdev->stream_lock); if (!atomic_read(&cifdev->fh_cnt)) rkcif_soft_reset(cifdev, true); @@ -4485,7 +5268,7 @@ static void rkcif_vb_done_oneframe(struct rkcif_stream *stream, stream->pixm.plane_fmt[i].sizeimage); } - if (stream->cifdev->hdr.mode == NO_HDR) + if (stream->cifdev->hdr.hdr_mode == NO_HDR) vb_done->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE); @@ -4659,6 +5442,40 @@ static int rkcif_dvp_g_ch_id(struct v4l2_device *v4l2_dev, return -EINVAL; } +static int rkcif_dvp_g_ch_id_by_fe(struct v4l2_device *v4l2_dev, + u32 intstat) +{ + if (intstat & DVP_ALL_END_ID0) { + if ((intstat & DVP_ALL_END_ID0) == + DVP_ALL_END_ID0) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID0\n"); + return RKCIF_STREAM_MIPI_ID0; + } + + if (intstat & DVP_ALL_END_ID1) { + if ((intstat & DVP_ALL_END_ID1) == + DVP_ALL_END_ID1) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID1\n"); + return RKCIF_STREAM_MIPI_ID1; + } + + if (intstat & DVP_ALL_END_ID2) { + if ((intstat & DVP_ALL_END_ID2) == + DVP_ALL_END_ID2) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID2\n"); + return RKCIF_STREAM_MIPI_ID2; + } + + if (intstat & DVP_ALL_END_ID3) { + if ((intstat & DVP_ALL_END_ID3) == + DVP_ALL_END_ID3) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID3\n"); + return RKCIF_STREAM_MIPI_ID3; + } + + return -EINVAL; +} + static bool rkcif_is_csi2_err_trigger_reset(struct rkcif_timer *timer) { struct rkcif_device *dev = container_of(timer, @@ -4827,6 +5644,7 @@ static void rkcif_dynamic_crop(struct rkcif_stream *stream) mbus = &cif_dev->active_sensor->mbus; if (mbus->type == V4L2_MBUS_CSI2_DPHY || + mbus->type == V4L2_MBUS_CSI2_CPHY || mbus->type == V4L2_MBUS_CCP2) { struct csi_channel_info *channel = &cif_dev->channels[stream->id]; @@ -4965,12 +5783,12 @@ static void rkcif_rdbk_frame_end(struct rkcif_stream *stream) u64 l_ts, m_ts, s_ts, time = 30000000LL; int ret, fps = -1; - if (dev->hdr.mode == HDR_X2) { + if (dev->hdr.hdr_mode == HDR_X2) { if (stream->id != RKCIF_STREAM_MIPI_ID1 || dev->stream[RKCIF_STREAM_MIPI_ID0].state != RKCIF_STATE_STREAMING || dev->stream[RKCIF_STREAM_MIPI_ID1].state != RKCIF_STATE_STREAMING) return; - } else if (dev->hdr.mode == HDR_X3) { + } else if (dev->hdr.hdr_mode == HDR_X3) { if (stream->id != RKCIF_STREAM_MIPI_ID2 || dev->stream[RKCIF_STREAM_MIPI_ID0].state != RKCIF_STATE_STREAMING || dev->stream[RKCIF_STREAM_MIPI_ID1].state != RKCIF_STATE_STREAMING || @@ -4983,7 +5801,7 @@ static void rkcif_rdbk_frame_end(struct rkcif_stream *stream) if (denominator && numerator) time = numerator * 1000 / denominator * 1000 * 1000; - if (dev->hdr.mode == HDR_X3) { + if (dev->hdr.hdr_mode == HDR_X3) { if (dev->rdbk_buf[RDBK_L] && dev->rdbk_buf[RDBK_M] && dev->rdbk_buf[RDBK_S]) { @@ -5036,7 +5854,7 @@ static void rkcif_rdbk_frame_end(struct rkcif_stream *stream) v4l2_err(&dev->v4l2_dev, "lost short frames\n"); goto RDBK_FRM_UNMATCH; } - } else if (dev->hdr.mode == HDR_X2) { + } else if (dev->hdr.hdr_mode == HDR_X2) { if (dev->rdbk_buf[RDBK_L] && dev->rdbk_buf[RDBK_M]) { l_ts = dev->rdbk_buf[RDBK_L]->vb.vb2_buf.timestamp; s_ts = dev->rdbk_buf[RDBK_M]->vb.vb2_buf.timestamp; @@ -5134,7 +5952,7 @@ static void rkcif_buf_done_prepare(struct rkcif_stream *stream, vb_done->sequence /= 2; } - if (cif_dev->hdr.mode == NO_HDR) { + if (cif_dev->hdr.hdr_mode == NO_HDR) { if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { if (stream->frame_phase == CIF_CSI_FRAME1_READY && active_buf) rkcif_vb_done_oneframe(stream, vb_done); @@ -5149,7 +5967,7 @@ static void rkcif_buf_done_prepare(struct rkcif_stream *stream, if (cif_dev->rdbk_buf[RDBK_L]) { v4l2_err(&cif_dev->v4l2_dev, "multiple long data in %s frame,frm_idx:%d,state:0x%x\n", - cif_dev->hdr.mode == HDR_X2 ? "hdr_x2" : "hdr_x3", + cif_dev->hdr.hdr_mode == HDR_X2 ? "hdr_x2" : "hdr_x3", stream->frame_idx, cif_dev->rdbk_buf[RDBK_L]->vb.vb2_buf.state); cif_dev->rdbk_buf[RDBK_L]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; @@ -5161,7 +5979,7 @@ static void rkcif_buf_done_prepare(struct rkcif_stream *stream, if (cif_dev->rdbk_buf[RDBK_M]) { v4l2_err(&cif_dev->v4l2_dev, "multiple %s frame,frm_idx:%d,state:0x%x\n", - cif_dev->hdr.mode == HDR_X2 ? "short data in hdr_x2" : "medium data in hdr_x3", + cif_dev->hdr.hdr_mode == HDR_X2 ? "short data in hdr_x2" : "medium data in hdr_x3", stream->frame_idx, cif_dev->rdbk_buf[RDBK_M]->vb.vb2_buf.state); cif_dev->rdbk_buf[RDBK_M]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; @@ -5169,13 +5987,13 @@ static void rkcif_buf_done_prepare(struct rkcif_stream *stream, } if (active_buf) cif_dev->rdbk_buf[RDBK_M] = active_buf; - if (cif_dev->hdr.mode == HDR_X2) + if (cif_dev->hdr.hdr_mode == HDR_X2) rkcif_rdbk_frame_end(stream); } else if (mipi_id == RKCIF_STREAM_MIPI_ID2) { if (cif_dev->rdbk_buf[RDBK_S]) { v4l2_err(&cif_dev->v4l2_dev, "multiple %s frame, frm_idx:%d,state:0x%x\n", - cif_dev->hdr.mode == HDR_X2 ? "err short data in hdr_x3" : "short data in hdr_x3", + cif_dev->hdr.hdr_mode == HDR_X2 ? "err short data in hdr_x3" : "short data in hdr_x3", stream->frame_idx, cif_dev->rdbk_buf[RDBK_S]->vb.vb2_buf.state); cif_dev->rdbk_buf[RDBK_S]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; @@ -5183,7 +6001,7 @@ static void rkcif_buf_done_prepare(struct rkcif_stream *stream, } if (active_buf) cif_dev->rdbk_buf[RDBK_S] = active_buf; - if (cif_dev->hdr.mode == HDR_X3) + if (cif_dev->hdr.hdr_mode == HDR_X3) rkcif_rdbk_frame_end(stream); } spin_unlock_irqrestore(&cif_dev->hdr_lock, flags); @@ -5248,14 +6066,14 @@ static void rkcif_deal_readout_time(struct rkcif_stream *stream) if (stream->id == RKCIF_STREAM_MIPI_ID0) detect_stream->readout.readout_time = stream->readout.fe_timestamp - stream->readout.fs_timestamp; - if ((cif_dev->hdr.mode == NO_HDR) && (stream->id == RKCIF_STREAM_MIPI_ID0)) { + if ((cif_dev->hdr.hdr_mode == NO_HDR) && (stream->id == RKCIF_STREAM_MIPI_ID0)) { detect_stream->readout.early_time = stream->readout.fe_timestamp - stream->readout.wk_timestamp; - } else if ((cif_dev->hdr.mode == HDR_X2) && (stream->id == RKCIF_STREAM_MIPI_ID1)) { + } else if ((cif_dev->hdr.hdr_mode == HDR_X2) && (stream->id == RKCIF_STREAM_MIPI_ID1)) { detect_stream->readout.early_time = stream->readout.fe_timestamp - stream->readout.wk_timestamp; detect_stream->readout.total_time = stream->readout.fe_timestamp - detect_stream->readout.fe_timestamp; detect_stream->readout.total_time += detect_stream->readout.readout_time; - } else if ((cif_dev->hdr.mode == HDR_X3) && (stream->id == RKCIF_STREAM_MIPI_ID2)) { + } else if ((cif_dev->hdr.hdr_mode == HDR_X3) && (stream->id == RKCIF_STREAM_MIPI_ID2)) { detect_stream->readout.early_time = stream->readout.fe_timestamp - stream->readout.wk_timestamp; detect_stream->readout.total_time = stream->readout.fe_timestamp - detect_stream->readout.fe_timestamp; detect_stream->readout.total_time += detect_stream->readout.readout_time; @@ -5325,12 +6143,39 @@ end: stream->frame_idx++; } +static void rkcif_update_stream_toisp(struct rkcif_device *cif_dev, + struct rkcif_stream *stream, + int mipi_id) +{ + if (stream->frame_phase == (CIF_CSI_FRAME0_READY | CIF_CSI_FRAME1_READY)) { + + v4l2_err(&cif_dev->v4l2_dev, "stream[%d], frm0/frm1 end simultaneously,frm id:%d\n", + stream->id, stream->frame_idx); + + stream->frame_idx++; + return; + } + + spin_lock(&stream->fps_lock); + if (stream->frame_phase & CIF_CSI_FRAME0_READY) + stream->fps_stats.frm0_timestamp = ktime_get_ns(); + else if (stream->frame_phase & CIF_CSI_FRAME1_READY) + stream->fps_stats.frm1_timestamp = ktime_get_ns(); + spin_unlock(&stream->fps_lock); + + rkcif_assign_new_buffer_pingpong_toisp(stream, + RKCIF_YUV_ADDR_STATE_UPDATE, + mipi_id); + stream->frame_idx++; +} + u32 rkcif_get_sof(struct rkcif_device *cif_dev) { u32 val = 0x0; struct rkcif_sensor_info *sensor = cif_dev->active_sensor; - if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) + if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + sensor->mbus.type == V4L2_MBUS_CSI2_CPHY) val = rkcif_csi2_get_sof(); else if (sensor->mbus.type == V4L2_MBUS_CCP2) val = rkcif_lvds_get_sof(cif_dev); @@ -5462,7 +6307,7 @@ static int rkcif_do_reset_work(struct rkcif_device *cif_dev, stream->curr_buf = NULL; stream->next_buf = NULL; } - ret = rkcif_csi_stream_start(stream); + ret = rkcif_csi_stream_start(stream, RKCIF_STREAM_MODE_CAPTURE); if (ret) { v4l2_err(&cif_dev->v4l2_dev, "%s:resume stream[%d] failed\n", __func__, stream->id); @@ -5650,14 +6495,14 @@ void rkcif_reset_watchdog_timer_handler(struct timer_list *t) int ret, is_reset = 0; struct rkmodule_vicap_reset_info rst_info; - if (dev->hdr.mode == NO_HDR) { + if (dev->hdr.hdr_mode == NO_HDR) { i = 0; if (dev->stream[i].state != RKCIF_STATE_STREAMING) goto end_detect; } else { - if (dev->hdr.mode == HDR_X3) + if (dev->hdr.hdr_mode == HDR_X3) stream_num = 3; - else if (dev->hdr.mode == HDR_X2) + else if (dev->hdr.hdr_mode == HDR_X2) stream_num = 2; for (i = 0; i < stream_num; i++) { @@ -5803,6 +6648,10 @@ static void rkcif_modify_line_int(struct rkcif_stream *stream, bool en) static void rkcif_detect_wake_up_mode_change(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; + struct sditf_priv *priv = cif_dev->sditf; + + if (priv->toisp_inf.link_mode != TOISP_NONE) + return; if (cif_dev->wait_line && (!stream->is_line_wake_up)) { stream->is_line_wake_up = true; @@ -5818,19 +6667,19 @@ static void rkcif_detect_wake_up_mode_change(struct rkcif_stream *stream) stream->is_line_inten = true; } - if (cif_dev->hdr.mode == NO_HDR && stream->id == RKCIF_STREAM_MIPI_ID0) { + if (cif_dev->hdr.hdr_mode == NO_HDR && stream->id == RKCIF_STREAM_MIPI_ID0) { if (cif_dev->wait_line != cif_dev->wait_line_cache) cif_dev->wait_line = cif_dev->wait_line_cache; - } else if (cif_dev->hdr.mode == HDR_X2 && stream->id == RKCIF_STREAM_MIPI_ID1) { + } else if (cif_dev->hdr.hdr_mode == HDR_X2 && stream->id == RKCIF_STREAM_MIPI_ID1) { if (cif_dev->wait_line != cif_dev->wait_line_cache) cif_dev->wait_line = cif_dev->wait_line_cache; - } else if (cif_dev->hdr.mode == HDR_X3 && stream->id == RKCIF_STREAM_MIPI_ID2) { + } else if (cif_dev->hdr.hdr_mode == HDR_X3 && stream->id == RKCIF_STREAM_MIPI_ID2) { if (cif_dev->wait_line != cif_dev->wait_line_cache) cif_dev->wait_line = cif_dev->wait_line_cache; } } -static u32 rkisp_mbus_pixelcode_to_v4l2(u32 pixelcode) +u32 rkcif_mbus_pixelcode_to_v4l2(u32 pixelcode) { u32 pixelformat; @@ -5919,13 +6768,14 @@ void rkcif_set_default_fmt(struct rkcif_device *cif_dev) v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, &fmt); memset(&pixm, 0, sizeof(pixm)); - pixm.pixelformat = rkisp_mbus_pixelcode_to_v4l2(fmt.format.code); + pixm.pixelformat = rkcif_mbus_pixelcode_to_v4l2(fmt.format.code); pixm.width = fmt.format.width; pixm.height = fmt.format.height; memset(&input_sel, 0, sizeof(input_sel)); input_sel.pad = i; input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; + input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); @@ -5938,6 +6788,451 @@ void rkcif_set_default_fmt(struct rkcif_device *cif_dev) } } +static void rkcif_enable_dma_capture(struct rkcif_stream *stream) +{ + struct rkcif_device *cif_dev = stream->cifdev; + struct v4l2_mbus_config *mbus_cfg = &cif_dev->active_sensor->mbus; + u32 val = 0; + + if (stream->dma_en) { + stream->dma_en |= stream->to_en_dma; + stream->to_en_dma = 0; + return; + } + + stream->dma_en |= stream->to_en_dma; + if (stream->to_en_dma == RKCIF_DMAEN_BY_VICAP) + rkcif_assign_new_buffer_pingpong(stream, + RKCIF_YUV_ADDR_STATE_INIT, + stream->id); + else if (stream->to_en_dma == RKCIF_DMAEN_BY_ISP) + rkcif_assign_new_buffer_pingpong_toisp(stream, + RKCIF_YUV_ADDR_STATE_INIT, + stream->id); + + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || + mbus_cfg->type == V4L2_MBUS_CCP2) { + val = rkcif_read_register(cif_dev, get_reg_index_of_id_ctrl0(stream->id)); + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY) + val |= CSI_ENABLE_CAPTURE; + else + val |= LVDS_ENABLE_CAPTURE; + + rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(stream->id), val); + } else { + val = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL); + rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, + val | ENABLE_CAPTURE); + } + stream->to_en_dma = 0; +} + +static void rkcif_stop_dma_capture(struct rkcif_stream *stream) +{ + struct rkcif_device *cif_dev = stream->cifdev; + struct v4l2_mbus_config *mbus_cfg = &cif_dev->active_sensor->mbus; + u32 val = 0; + + stream->dma_en &= ~stream->to_stop_dma; + if (stream->dma_en != 0) { + stream->to_stop_dma = 0; + return; + } + + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || + mbus_cfg->type == V4L2_MBUS_CCP2) { + val = rkcif_read_register(cif_dev, get_reg_index_of_id_ctrl0(stream->id)); + if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || + mbus_cfg->type == V4L2_MBUS_CSI2_CPHY) + val &= ~CSI_ENABLE_CAPTURE; + else + val &= ~LVDS_ENABLE_CAPTURE; + + rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(stream->id), val); + } else { + if (atomic_read(&cif_dev->pipe.stream_cnt) == 1) { + val = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL); + rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, + val & (~ENABLE_CAPTURE)); + } + } + stream->to_stop_dma = 0; +} + +static int rkcif_g_toisp_ch(unsigned int intstat_glb, int index) +{ + if (intstat_glb & TOISP_END_CH0(index)) + return RKCIF_TOISP_CH0; + if (intstat_glb & TOISP_END_CH1(index)) + return RKCIF_TOISP_CH1; + if (intstat_glb & TOISP_END_CH2(index)) + return RKCIF_TOISP_CH2; + + return -EINVAL; +} + +static void rkcif_toisp_check_stop_status(struct sditf_priv *priv, + unsigned int intstat_glb, + int index) +{ + int ch = 0; + struct rkcif_stream *stream; + int src_id = 0; + int i = 0; + u32 val = 0; + + for (i = 0; i < TOISP_CH_MAX; i++) { + ch = rkcif_g_toisp_ch(intstat_glb, index); + if (ch < 0) + continue; + src_id = priv->toisp_inf.ch_info[ch].id; + if (src_id == 24) + stream = &priv->cif_dev->stream[0]; + else + stream = &priv->cif_dev->stream[src_id % 4]; + if (stream->stopping) { + v4l2_dbg(3, rkcif_debug, &priv->cif_dev->v4l2_dev, + "stream[%d] stop\n", + stream->id); + rkcif_stream_stop(stream); + stream->stopping = false; + wake_up(&stream->wq_stopped); + } + switch (ch) { + case RKCIF_TOISP_CH0: + val = TOISP_END_CH0(index); + intstat_glb = intstat_glb & (~val); + break; + case RKCIF_TOISP_CH1: + val = TOISP_END_CH1(index); + intstat_glb = intstat_glb & (~val); + break; + case RKCIF_TOISP_CH2: + val = TOISP_END_CH2(index); + intstat_glb = intstat_glb & (~val); + break; + default: + break; + } + } +} + +void rkcif_irq_handle_toisp(struct rkcif_device *cif_dev, unsigned int intstat_glb) +{ + int i = 0; + bool to_check = false; + struct sditf_priv *priv = cif_dev->sditf; + + if (priv->toisp_inf.link_mode == TOISP_NONE) + return; + + for (i = 0; i < 2; i++) { + if (priv->toisp_inf.link_mode == TOISP0 && + i == 0) { + to_check = true; + } else if (priv->toisp_inf.link_mode == TOISP1 && + i == 1) { + to_check = true; + } else if (priv->toisp_inf.link_mode == TOISP_UNITE) { + to_check = true; + } + if (to_check) + rkcif_toisp_check_stop_status(priv, intstat_glb, i); + } +} + +static void rkcif_deal_sof(struct rkcif_device *cif_dev) +{ + struct rkcif_stream *detect_stream = &cif_dev->stream[0]; + struct v4l2_mbus_config *mbus = &cif_dev->active_sensor->mbus; + unsigned long flags; + + if (mbus->type == V4L2_MBUS_CSI2_DPHY || + mbus->type == V4L2_MBUS_CSI2_CPHY) + rkcif_csi2_event_inc_sof(); + else if (mbus->type == V4L2_MBUS_CCP2) + rkcif_lvds_event_inc_sof(cif_dev); + else + rkcif_dvp_event_inc_sof(cif_dev); + + detect_stream->fs_cnt_in_single_frame++; + spin_lock_irqsave(&detect_stream->fps_lock, flags); + detect_stream->readout.fs_timestamp = ktime_get_ns(); + spin_unlock_irqrestore(&detect_stream->fps_lock, flags); +} + +unsigned int rkcif_irq_global(struct rkcif_device *cif_dev) +{ + unsigned int intstat_glb = 0; + + intstat_glb = rkcif_read_register(cif_dev, CIF_REG_GLB_INTST); + if (intstat_glb) + rkcif_write_register(cif_dev, CIF_REG_GLB_INTST, intstat_glb); + + if (intstat_glb & SCALE_TOISP_AXI0_ERR) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: scale channel, AXI0 bus err intstat_glb:0x%x !!\n", + intstat_glb); + return 0; + } + if (intstat_glb & SCALE_TOISP_AXI1_ERR) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: scale channel, AXI1 bus err intstat_glb:0x%x !!\n", + intstat_glb); + return 0; + } + rkcif_irq_handle_scale(cif_dev, intstat_glb); + v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, + "intstat_glb 0x%x\n", + intstat_glb); + return intstat_glb; +} + +/* pingpong irq for rk3588 and next */ +void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev) +{ + struct rkcif_stream *stream; + struct rkcif_stream *detect_stream = &cif_dev->stream[0]; + struct v4l2_mbus_config *mbus; + unsigned int intstat, i = 0xff, bak_intstat = 0; + + if (!cif_dev->active_sensor) + return; + + mbus = &cif_dev->active_sensor->mbus; + if (mbus->type == V4L2_MBUS_CSI2_DPHY || + mbus->type == V4L2_MBUS_CSI2_CPHY || + mbus->type == V4L2_MBUS_CCP2) { + int mipi_id; + u32 lastline = 0; + + intstat = rkcif_read_register(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT); + lastline = rkcif_read_register(cif_dev, CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1); + + /* clear all interrupts that has been triggered */ + if (intstat) { + bak_intstat = intstat; + rkcif_write_register(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT, intstat); + v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, + "intstat 0x%x\n", + intstat); + } else { + return; + } + + if (intstat & CSI_SIZE_ERR) { + cif_dev->irq_stats.csi_size_err_cnt++; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: csi size err, intstat:0x%x, lastline:%d!!\n", + intstat, lastline); + return; + } + + if (intstat & CSI_FIFO_OVERFLOW_V1) { + cif_dev->irq_stats.csi_overflow_cnt++; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: csi fifo overflow, intstat:0x%x, lastline:%d!!\n", + intstat, lastline); + return; + } + + if (intstat & CSI_BANDWIDTH_LACK_V1) { + cif_dev->irq_stats.csi_bwidth_lack_cnt++; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: csi bandwidth lack, intstat:0x%x!!\n", + intstat); + return; + } + + if (intstat & CSI_ALL_ERROR_INTEN_V1) { + cif_dev->irq_stats.all_err_cnt++; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: CSI_ALL_ERROR_INTEN:0x%x!!\n", intstat); + return; + } + + if (intstat & CSI_FRAME0_START_ID0 || intstat & CSI_FRAME1_START_ID0) + rkcif_deal_sof(cif_dev); + + for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { + if (intstat & CSI_LINE_INTSTAT(i)) { + stream = &cif_dev->stream[i]; + if (stream->is_line_inten) { + stream->line_int_cnt++; + rkcif_line_wake_up(stream, stream->id); + rkcif_modify_line_int(stream, false); + stream->is_line_inten = false; + } + v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, + "%s: id0 cur line:%d\n", __func__, lastline & 0x3fff); + } + } + + /* if do not reach frame dma end, return irq */ + mipi_id = rkcif_csi_g_mipi_id(&cif_dev->v4l2_dev, intstat); + if (mipi_id < 0) + return; + + for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { + mipi_id = rkcif_csi_g_mipi_id(&cif_dev->v4l2_dev, + intstat); + if (mipi_id < 0) + continue; + + stream = &cif_dev->stream[mipi_id]; + + if (stream->stopping && + !(stream->cur_stream_mode & RKCIF_STREAM_MODE_TOISP)) { + rkcif_stream_stop(stream); + stream->stopping = false; + wake_up(&stream->wq_stopped); + continue; + } + + if (stream->state != RKCIF_STATE_STREAMING) + continue; + + switch (mipi_id) { + case RKCIF_STREAM_MIPI_ID0: + stream->frame_phase = SW_FRM_END_ID0(intstat); + intstat &= ~CSI_FRAME_END_ID0; + break; + case RKCIF_STREAM_MIPI_ID1: + stream->frame_phase = SW_FRM_END_ID1(intstat); + intstat &= ~CSI_FRAME_END_ID1; + break; + case RKCIF_STREAM_MIPI_ID2: + stream->frame_phase = SW_FRM_END_ID2(intstat); + intstat &= ~CSI_FRAME_END_ID2; + break; + case RKCIF_STREAM_MIPI_ID3: + stream->frame_phase = SW_FRM_END_ID3(intstat); + intstat &= ~CSI_FRAME_END_ID3; + break; + } + if (stream->crop_dyn_en) + rkcif_dynamic_crop(stream); + + if (stream->dma_en & RKCIF_DMAEN_BY_VICAP) + rkcif_update_stream(cif_dev, stream, mipi_id); + else if (stream->dma_en & RKCIF_DMAEN_BY_ISP) + rkcif_update_stream_toisp(cif_dev, stream, mipi_id); + + if (stream->to_en_dma) + rkcif_enable_dma_capture(stream); + if (stream->to_stop_dma) { + rkcif_stop_dma_capture(stream); + wake_up(&stream->wq_stopped); + } + rkcif_detect_wake_up_mode_change(stream); + if (mipi_id == RKCIF_STREAM_MIPI_ID0) { + if ((intstat & (CSI_FRAME1_START_ID0 | CSI_FRAME0_START_ID0)) == 0 && + detect_stream->fs_cnt_in_single_frame > 1) { + v4l2_err(&cif_dev->v4l2_dev, + "%s ERR: multi fs in oneframe, bak_int:0x%x, fs_num:%u\n", + __func__, + bak_intstat, + detect_stream->fs_cnt_in_single_frame); + detect_stream->is_fs_fe_not_paired = true; + detect_stream->fs_cnt_in_single_frame = 0; + } else { + detect_stream->fs_cnt_in_single_frame--; + } + } + rkcif_monitor_reset_event(cif_dev); + } + cif_dev->irq_stats.all_frm_end_cnt++; + } else { + struct rkcif_stream *stream; + int ch_id; + + intstat = rkcif_read_register(cif_dev, CIF_REG_DVP_INTSTAT); + + rkcif_write_register(cif_dev, CIF_REG_DVP_INTSTAT, intstat); + + stream = &cif_dev->stream[RKCIF_STREAM_CIF]; + + if (intstat & DVP_FRAME0_START_ID0 || intstat & DVP_FRAME1_START_ID0) + rkcif_deal_sof(cif_dev); + + if (intstat & DVP_SIZE_ERR) { + cif_dev->irq_stats.dvp_size_err_cnt++; + v4l2_info(&cif_dev->v4l2_dev, "dvp size err intstat 0x%x\n", intstat); + } + + if (intstat & DVP_FIFO_OVERFLOW) { + cif_dev->irq_stats.dvp_overflow_cnt++; + v4l2_info(&cif_dev->v4l2_dev, "dvp overflow intstat 0x%x\n", intstat); + } + + if (intstat & DVP_BANDWIDTH_LACK) { + cif_dev->irq_stats.dvp_bwidth_lack_cnt++; + v4l2_info(&cif_dev->v4l2_dev, + "dvp bandwidth lack err intstat 0x%x\n", + intstat); + } + + if (intstat & INTSTAT_ERR) { + cif_dev->irq_stats.all_err_cnt++; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: DVP_ALL_ERROR_INTEN:0x%x!!\n", intstat); + } + + for (i = 0; i < RKCIF_MAX_STREAM_DVP; i++) { + ch_id = rkcif_dvp_g_ch_id_by_fe(&cif_dev->v4l2_dev, intstat); + + if (ch_id < 0) + continue; + + stream = &cif_dev->stream[ch_id]; + + if (stream->stopping) { + rkcif_stream_stop(stream); + stream->stopping = false; + wake_up(&stream->wq_stopped); + continue; + } + + if (stream->state != RKCIF_STATE_STREAMING) + continue; + + switch (ch_id) { + case RKCIF_STREAM_MIPI_ID0: + stream->frame_phase = SW_FRM_END_ID0(intstat); + intstat &= ~DVP_ALL_END_ID0; + break; + case RKCIF_STREAM_MIPI_ID1: + stream->frame_phase = SW_FRM_END_ID1(intstat); + intstat &= ~DVP_ALL_END_ID1; + break; + case RKCIF_STREAM_MIPI_ID2: + stream->frame_phase = SW_FRM_END_ID2(intstat); + intstat &= ~DVP_ALL_END_ID2; + break; + case RKCIF_STREAM_MIPI_ID3: + stream->frame_phase = SW_FRM_END_ID3(intstat); + intstat &= ~DVP_ALL_END_ID3; + break; + } + if (stream->dma_en) + rkcif_update_stream(cif_dev, stream, ch_id); + if (stream->to_en_dma) + rkcif_enable_dma_capture(stream); + if (stream->to_stop_dma) { + rkcif_stop_dma_capture(stream); + wake_up(&stream->wq_stopped); + } + cif_dev->irq_stats.all_frm_end_cnt++; + } + + if (stream->crop_dyn_en) + rkcif_dynamic_crop(stream); + } +} + void rkcif_irq_pingpong(struct rkcif_device *cif_dev) { struct rkcif_stream *stream; @@ -5952,6 +7247,7 @@ void rkcif_irq_pingpong(struct rkcif_device *cif_dev) mbus = &cif_dev->active_sensor->mbus; if ((mbus->type == V4L2_MBUS_CSI2_DPHY || + mbus->type == V4L2_MBUS_CSI2_CPHY || mbus->type == V4L2_MBUS_CCP2) && (cif_dev->chip_id == CHIP_RK1808_CIF || cif_dev->chip_id == CHIP_RV1126_CIF || @@ -5994,27 +7290,9 @@ void rkcif_irq_pingpong(struct rkcif_device *cif_dev) __func__); } - if (intstat & CSI_FRAME0_START_ID0) { - if (mbus->type == V4L2_MBUS_CSI2_DPHY) - rkcif_csi2_event_inc_sof(); - else if (mbus->type == V4L2_MBUS_CCP2) - rkcif_lvds_event_inc_sof(cif_dev); - detect_stream->fs_cnt_in_single_frame++; - spin_lock_irqsave(&detect_stream->fps_lock, flags); - detect_stream->readout.fs_timestamp = ktime_get_ns(); - spin_unlock_irqrestore(&detect_stream->fps_lock, flags); - } + if (intstat & CSI_FRAME0_START_ID0 || intstat & CSI_FRAME1_START_ID0) + rkcif_deal_sof(cif_dev); - if (intstat & CSI_FRAME1_START_ID0) { - if (mbus->type == V4L2_MBUS_CSI2_DPHY) - rkcif_csi2_event_inc_sof(); - else if (mbus->type == V4L2_MBUS_CCP2) - rkcif_lvds_event_inc_sof(cif_dev); - detect_stream->fs_cnt_in_single_frame++; - spin_lock_irqsave(&detect_stream->fps_lock, flags); - detect_stream->readout.fs_timestamp = ktime_get_ns(); - spin_unlock_irqrestore(&detect_stream->fps_lock, flags); - } for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { if (intstat & CSI_LINE_INTSTAT(i)) { stream = &cif_dev->stream[i]; @@ -6114,7 +7392,7 @@ void rkcif_irq_pingpong(struct rkcif_device *cif_dev) (cif_dev->dvp_sof_in_oneframe == 0)) { if ((intstat & (PRE_INF_FRAME_END | PST_INF_FRAME_END)) == 0x0) { if ((intstat & INTSTAT_ERR) == 0x0) { - rkcif_dvp_event_inc_sof(cif_dev); + rkcif_deal_sof(cif_dev); int_en = rkcif_read_register(cif_dev, CIF_REG_DVP_INTEN); int_en &= ~LINE_INT_EN; rkcif_write_register(cif_dev, CIF_REG_DVP_INTEN, int_en); @@ -6380,3 +7658,4 @@ void rkcif_irq_lite_lvds(struct rkcif_device *cif_dev) cif_dev->irq_stats.all_frm_end_cnt++; } } + diff --git a/drivers/media/platform/rockchip/cif/cif-luma.c b/drivers/media/platform/rockchip/cif/cif-luma.c index 774001506ab4..9d751df3c4e5 100644 --- a/drivers/media/platform/rockchip/cif/cif-luma.c +++ b/drivers/media/platform/rockchip/cif/cif-luma.c @@ -289,7 +289,7 @@ static void rkcif_luma_readout_task(unsigned long data) void rkcif_luma_isr(struct rkcif_luma_vdev *luma_vdev, int mipi_id, u32 frame_id) { - u8 hdr_mode = luma_vdev->cifdev->hdr.mode; + u8 hdr_mode = luma_vdev->cifdev->hdr.hdr_mode; enum rkcif_luma_frm_mode frm_mode; bool send_task; u32 i, value; diff --git a/drivers/media/platform/rockchip/cif/cif-scale.c b/drivers/media/platform/rockchip/cif/cif-scale.c new file mode 100644 index 000000000000..7fd33809c5ce --- /dev/null +++ b/drivers/media/platform/rockchip/cif/cif-scale.c @@ -0,0 +1,1171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dev.h" +#include "regs.h" +#include "mipi-csi2.h" +#include +#include + +#define MEMORY_ALIGN_ROUND_UP_HEIGHT 16 + +#define SCALE_MIN_WIDTH 4 +#define SCALE_MIN_HEIGHT 4 +#define SCALE_OUTPUT_STEP_WISE 1 +#define CIF_SCALE_REQ_BUFS_MIN 3 + +static const struct cif_output_fmt scale_out_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .fmt_type = CIF_FMT_TYPE_RAW, + } +}; + +static int rkcif_scale_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct cif_output_fmt *fmt = NULL; + + if (f->index >= ARRAY_SIZE(scale_out_fmts)) + return -EINVAL; + fmt = &scale_out_fmts[f->index]; + f->pixelformat = fmt->fourcc; + return 0; +} + +static int rkcif_scale_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + + f->fmt.pix_mp = scale_vdev->pixm; + return 0; +} + +static u32 rkcif_scale_align_bits_per_pixel(struct rkcif_device *cif_dev, + const struct cif_output_fmt *fmt, + int plane_index) +{ + u32 bpp = 0, i; + + if (fmt) { + switch (fmt->fourcc) { + case V4L2_PIX_FMT_SBGGR16: + case V4L2_PIX_FMT_SGBRG16: + case V4L2_PIX_FMT_SGRBG16: + case V4L2_PIX_FMT_SRGGB16: + bpp = max(fmt->bpp[plane_index], (u8)CIF_RAW_STORED_BIT_WIDTH_RV1126); + for (i = 1; i < 5; i++) { + if (i * CIF_RAW_STORED_BIT_WIDTH_RV1126 >= bpp) { + bpp = i * CIF_RAW_STORED_BIT_WIDTH_RV1126; + break; + } + } + break; + default: + v4l2_err(&cif_dev->v4l2_dev, "fourcc: %d is not supported!\n", + fmt->fourcc); + break; + } + } + + return bpp; +} + + +static const struct +cif_output_fmt *find_output_fmt(u32 pixelfmt) +{ + const struct cif_output_fmt *fmt; + u32 i; + + for (i = 0; i < ARRAY_SIZE(scale_out_fmts); i++) { + fmt = &scale_out_fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + + return NULL; +} + +static int rkcif_scale_set_fmt(struct rkcif_scale_vdev *scale_vdev, + struct v4l2_pix_format_mplane *pixm, + bool try) +{ + struct rkcif_stream *stream = scale_vdev->stream; + struct rkcif_device *cif_dev = scale_vdev->cifdev; + struct v4l2_subdev_selection input_sel; + struct v4l2_subdev_format fmt_src; + const struct cif_output_fmt *fmt; + unsigned int imagesize = 0; + int bpl, size, bpp; + int scale_times = 0; + u32 scale_ratio = 0; + u32 width = 640; + u32 height = 480; + int ret = 0; + + if (!cif_dev->terminal_sensor.sd) + rkcif_update_sensor_info(&cif_dev->stream[0]); + + if (cif_dev->terminal_sensor.sd) { + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, &fmt_src); + if (ret) { + v4l2_err(&scale_vdev->cifdev->v4l2_dev, + "%s: get sensor format failed\n", __func__); + return ret; + } + + input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; + input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + input_sel.pad = 0; + ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, + pad, get_selection, NULL, + &input_sel); + if (!ret) { + fmt_src.format.width = input_sel.r.width; + fmt_src.format.height = input_sel.r.height; + } + scale_vdev->src_res.width = fmt_src.format.width; + scale_vdev->src_res.height = fmt_src.format.height; + } + fmt = find_output_fmt(pixm->pixelformat); + if (fmt == NULL) { + v4l2_err(&scale_vdev->cifdev->v4l2_dev, + "format of source channel are not bayer raw, not support scale\n"); + return -1; + } + if (scale_vdev->src_res.width && scale_vdev->src_res.height) { + width = scale_vdev->src_res.width; + height = scale_vdev->src_res.height; + } + scale_ratio = width / pixm->width; + if (scale_ratio <= 8) { + scale_vdev->scale_mode = SCALE_8TIMES; + scale_times = 8; + } else if (scale_ratio <= 16) { + scale_vdev->scale_mode = SCALE_16TIMES; + scale_times = 16; + } else { + scale_vdev->scale_mode = SCALE_32TIMES; + scale_times = 32; + } + //source resolution align (scale_times * 2) + pixm->width = width / (scale_times * 2) * 2; + pixm->height = height / (scale_times * 2) * 2; + pixm->num_planes = fmt->mplanes; + pixm->field = V4L2_FIELD_NONE; + pixm->quantization = V4L2_QUANTIZATION_DEFAULT; + + bpp = rkcif_scale_align_bits_per_pixel(cif_dev, fmt, 0); + bpl = pixm->width * bpp / CIF_RAW_STORED_BIT_WIDTH_RV1126; + size = bpl * pixm->height; + imagesize += size; + + v4l2_dbg(3, rkcif_debug, &stream->cifdev->v4l2_dev, + "%s C-Plane %i size: %d, Total imagesize: %d\n", + __func__, 0, size, imagesize); + + if (fmt->mplanes == 1) { + pixm->plane_fmt[0].bytesperline = bpl; + pixm->plane_fmt[0].sizeimage = imagesize; + } + + if (!try) { + scale_vdev->scale_out_fmt = fmt; + scale_vdev->pixm = *pixm; + + v4l2_dbg(3, rkcif_debug, &stream->cifdev->v4l2_dev, + "%s: req(%d, %d) src out(%d, %d)\n", __func__, + pixm->width, pixm->height, + scale_vdev->src_res.width, scale_vdev->src_res.height); + } + return 0; +} + +static int rkcif_scale_s_fmt_vid_cap_mplane(struct file *file, + void *priv, struct v4l2_format *f) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + int ret = 0; + + if (vb2_is_busy(&scale_vdev->vnode.buf_queue)) { + v4l2_err(&scale_vdev->cifdev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = rkcif_scale_set_fmt(scale_vdev, &f->fmt.pix_mp, false); + + return ret; +} + +static int rkcif_scale_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + struct device *dev = scale_vdev->cifdev->dev; + + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + strscpy(cap->card, dev->driver->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + return 0; +} + +static long rkcif_scale_ioctl_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + struct rkcif_device *dev = scale_vdev->cifdev; + struct bayer_blc *pblc; + + switch (cmd) { + case RKCIF_CMD_GET_SCALE_BLC: + pblc = (struct bayer_blc *)arg; + *pblc = scale_vdev->blc; + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "get scale blc %d %d %d %d\n", + pblc->pattern00, pblc->pattern01, pblc->pattern02, pblc->pattern03); + break; + case RKCIF_CMD_SET_SCALE_BLC: + pblc = (struct bayer_blc *)arg; + scale_vdev->blc = *pblc; + v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "set scale blc %d %d %d %d\n", + pblc->pattern00, pblc->pattern01, pblc->pattern02, pblc->pattern03); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rkcif_scale_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + + if (input->index > 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int rkcif_scale_try_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + + rkcif_scale_set_fmt(scale_vdev, &f->fmt.pix_mp, true); + + return 0; +} + +static int rkcif_scale_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + struct rkcif_device *dev = scale_vdev->cifdev; + struct rkcif_sensor_info *sensor = &dev->terminal_sensor; + struct v4l2_subdev_frame_interval fi; + int ret; + + if (fival->index != 0) + return -EINVAL; + + if (!sensor || !sensor->sd) { + /* TODO: active_sensor is NULL if using DMARX path */ + v4l2_err(&dev->v4l2_dev, "%s Not active sensor\n", __func__); + return -ENODEV; + } + + ret = v4l2_subdev_call(sensor->sd, video, g_frame_interval, &fi); + if (ret && ret != -ENOIOCTLCMD) { + return ret; + } else if (ret == -ENOIOCTLCMD) { + /* Set a default value for sensors not implements ioctl */ + fi.interval.numerator = 1; + fi.interval.denominator = 30; + } + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.step.numerator = 1; + fival->stepwise.step.denominator = 1; + fival->stepwise.max.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.min.numerator = fi.interval.numerator; + fival->stepwise.min.denominator = fi.interval.denominator; + + return 0; +} + +static int rkcif_scale_enum_framesizes(struct file *file, void *prov, + struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_frmsize_discrete *s = &fsize->discrete; + struct rkcif_scale_vdev *scale_vdev = video_drvdata(file); + struct rkcif_device *dev = scale_vdev->cifdev; + struct v4l2_rect input_rect; + struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; + int vc = 0; + int scale_times = 0; + + if (fsize->index >= RKCIF_SCALE_ENUM_SIZE_MAX) + return -EINVAL; + + if (!find_output_fmt(fsize->pixel_format)) + return -EINVAL; + + input_rect.width = RKCIF_DEFAULT_WIDTH; + input_rect.height = RKCIF_DEFAULT_HEIGHT; + + if (terminal_sensor && terminal_sensor->sd) + get_input_fmt(terminal_sensor->sd, + &input_rect, 0, &vc); + + switch (fsize->index) { + case SCALE_8TIMES: + scale_times = 8; + break; + case SCALE_16TIMES: + scale_times = 16; + break; + case SCALE_32TIMES: + scale_times = 32; + break; + default: + scale_times = 32; + break; + } + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + s->width = input_rect.width / (scale_times * 2) * 2; + s->height = input_rect.height / (scale_times * 2) * 2; + + return 0; +} + +/* ISP video device IOCTLs */ +static const struct v4l2_ioctl_ops rkcif_scale_ioctl = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = rkcif_scale_enum_input, + .vidioc_enum_fmt_vid_cap = rkcif_scale_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = rkcif_scale_g_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = rkcif_scale_s_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = rkcif_scale_try_fmt_vid_cap_mplane, + .vidioc_querycap = rkcif_scale_querycap, + .vidioc_enum_frameintervals = rkcif_scale_enum_frameintervals, + .vidioc_enum_framesizes = rkcif_scale_enum_framesizes, + .vidioc_default = rkcif_scale_ioctl_default, +}; + +static int rkcif_scale_fh_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct rkcif_vdev_node *vnode = vdev_to_node(vdev); + struct rkcif_scale_vdev *scale_vdev = to_rkcif_scale_vdev(vnode); + int ret; + + ret = rkcif_update_sensor_info(scale_vdev->stream); + if (ret < 0) { + v4l2_err(vdev, + "update sensor info failed %d\n", + ret); + + return ret; + } + + ret = v4l2_fh_open(file); + if (!ret) { + ret = v4l2_pipeline_pm_get(&vnode->vdev.entity); + if (ret < 0) + vb2_fop_release(file); + } + + return ret; +} + +static int rkcif_scale_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct rkcif_vdev_node *vnode = vdev_to_node(vdev); + int ret; + + ret = vb2_fop_release(file); + if (!ret) + v4l2_pipeline_pm_put(&vnode->vdev.entity); + return ret; +} + +struct v4l2_file_operations rkcif_scale_fops = { + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .open = rkcif_scale_fh_open, + .release = rkcif_scale_fop_release +}; + +static int rkcif_scale_vb2_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkcif_scale_vdev *scale_vdev = queue->drv_priv; + struct rkcif_device *cif_dev = scale_vdev->cifdev; + const struct v4l2_pix_format_mplane *pixm = NULL; + const struct cif_output_fmt *cif_fmt; + u32 i; + const struct v4l2_plane_pix_format *plane_fmt; + + pixm = &scale_vdev->pixm; + cif_fmt = scale_vdev->scale_out_fmt; + *num_planes = cif_fmt->mplanes; + + for (i = 0; i < cif_fmt->mplanes; i++) { + plane_fmt = &pixm->plane_fmt[i]; + sizes[i] = plane_fmt->sizeimage; + } + + v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "%s count %d, size %d\n", + v4l2_type_names[queue->type], *num_buffers, sizes[0]); + return 0; + +} + +static void rkcif_scale_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkcif_buffer *cifbuf = to_rkcif_buffer(vbuf); + struct vb2_queue *queue = vb->vb2_queue; + struct rkcif_scale_vdev *scale_vdev = queue->drv_priv; + struct v4l2_pix_format_mplane *pixm = &scale_vdev->pixm; + const struct cif_output_fmt *fmt = scale_vdev->scale_out_fmt; + struct rkcif_hw *hw_dev = scale_vdev->cifdev->hw_dev; + unsigned long lock_flags = 0; + int i; + + memset(cifbuf->buff_addr, 0, sizeof(cifbuf->buff_addr)); + /* If mplanes > 1, every c-plane has its own m-plane, + * otherwise, multiple c-planes are in the same m-plane + */ + for (i = 0; i < fmt->mplanes; i++) { + void *addr = vb2_plane_vaddr(vb, i); + + if (hw_dev->iommu_en) { + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, i); + + cifbuf->buff_addr[i] = sg_dma_address(sgt->sgl); + } else { + cifbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + } + if (rkcif_debug && addr && !hw_dev->iommu_en) { + memset(addr, 0, pixm->plane_fmt[i].sizeimage); + v4l2_dbg(1, rkcif_debug, &scale_vdev->cifdev->v4l2_dev, + "Clear buffer, size: 0x%08x\n", + pixm->plane_fmt[i].sizeimage); + } + } + + if (fmt->mplanes == 1) { + for (i = 0; i < fmt->cplanes - 1; i++) + cifbuf->buff_addr[i + 1] = cifbuf->buff_addr[i] + + pixm->plane_fmt[i].bytesperline * pixm->height; + } + spin_lock_irqsave(&scale_vdev->vbq_lock, lock_flags); + list_add_tail(&cifbuf->queue, &scale_vdev->buf_head); + spin_unlock_irqrestore(&scale_vdev->vbq_lock, lock_flags); +} + +static int rkcif_scale_stop(struct rkcif_scale_vdev *scale_vdev) +{ + struct rkcif_device *dev = scale_vdev->cifdev; + int ch = scale_vdev->ch; + + rkcif_write_register_and(dev, CIF_REG_SCL_CH_CTRL, + ~(CIF_SCALE_EN(ch) | + CIF_SCALE_SW_SRC_CH(0x1f, ch) | + CIF_SCALE_SW_MODE(0x03, ch))); + scale_vdev->state = RKCIF_STATE_READY; + scale_vdev->frame_idx = 0; + return 0; +} + +static void rkcif_scale_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct rkcif_scale_vdev *scale_vdev = vq->drv_priv; + struct rkcif_stream *stream = scale_vdev->stream; + struct rkcif_device *dev = scale_vdev->cifdev; + struct rkcif_buffer *buf = NULL; + int ret = 0; + + mutex_lock(&dev->scale_lock); + /* Make sure no new work queued in isr before draining wq */ + scale_vdev->stopping = true; + ret = wait_event_timeout(scale_vdev->wq_stopped, + scale_vdev->state != RKCIF_STATE_STREAMING, + msecs_to_jiffies(1000)); + if (!ret) { + rkcif_scale_stop(scale_vdev); + scale_vdev->stopping = false; + } + /* release buffers */ + if (scale_vdev->curr_buf) + list_add_tail(&scale_vdev->curr_buf->queue, &scale_vdev->buf_head); + + if (scale_vdev->next_buf && + scale_vdev->next_buf != scale_vdev->curr_buf) + list_add_tail(&scale_vdev->next_buf->queue, &scale_vdev->buf_head); + scale_vdev->curr_buf = NULL; + scale_vdev->next_buf = NULL; + while (!list_empty(&scale_vdev->buf_head)) { + buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&dev->scale_lock); + rkcif_do_stop_stream(stream, RKCIF_STREAM_MODE_TOSCALE); +} + +static int rkcif_scale_channel_init(struct rkcif_scale_vdev *scale_vdev) +{ + struct rkcif_device *cif_dev = scale_vdev->cifdev; + struct rkcif_scale_ch_info *ch_info = &scale_vdev->ch_info; + struct v4l2_pix_format_mplane pixm = scale_vdev->pixm; + const struct cif_output_fmt *fmt = scale_vdev->scale_out_fmt; + + if (cif_dev->inf_id == RKCIF_DVP) + scale_vdev->ch_src = SCALE_DVP; + else + scale_vdev->ch_src = 4 * cif_dev->csi_host_idx + scale_vdev->ch; + ch_info->width = pixm.width; + ch_info->height = pixm.height; + ch_info->vir_width = ALIGN(ch_info->width * fmt->bpp[0] / 8, 8); + return 0; +} + +static enum cif_reg_index get_reg_index_of_scale_vlw(int ch) +{ + enum cif_reg_index index; + + switch (ch) { + case 0: + index = CIF_REG_SCL_VLW_CH0; + break; + case 1: + index = CIF_REG_SCL_VLW_CH1; + break; + case 2: + index = CIF_REG_SCL_VLW_CH2; + break; + case 3: + index = CIF_REG_SCL_VLW_CH3; + break; + default: + index = CIF_REG_SCL_VLW_CH0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_scale_frm0_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_SCL_FRM0_ADDR_CH0; + break; + case 1: + index = CIF_REG_SCL_FRM0_ADDR_CH1; + break; + case 2: + index = CIF_REG_SCL_FRM0_ADDR_CH2; + break; + case 3: + index = CIF_REG_SCL_FRM0_ADDR_CH3; + break; + default: + index = CIF_REG_SCL_FRM0_ADDR_CH0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_scale_frm1_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_SCL_FRM1_ADDR_CH0; + break; + case 1: + index = CIF_REG_SCL_FRM1_ADDR_CH1; + break; + case 2: + index = CIF_REG_SCL_FRM1_ADDR_CH2; + break; + case 3: + index = CIF_REG_SCL_FRM1_ADDR_CH3; + break; + default: + index = CIF_REG_SCL_FRM1_ADDR_CH0; + break; + } + + return index; +} + +static void rkcif_assign_scale_buffer_init(struct rkcif_scale_vdev *scale_vdev, + int ch) +{ + struct rkcif_device *dev = scale_vdev->stream->cifdev; + u32 frm0_addr; + u32 frm1_addr; + unsigned long flags; + + frm0_addr = get_reg_index_of_scale_frm0_addr(ch); + frm1_addr = get_reg_index_of_scale_frm1_addr(ch); + + spin_lock_irqsave(&scale_vdev->vbq_lock, flags); + + if (!scale_vdev->curr_buf) { + if (!list_empty(&scale_vdev->buf_head)) { + scale_vdev->curr_buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, + queue); + list_del(&scale_vdev->curr_buf->queue); + } + } + + if (scale_vdev->curr_buf) + rkcif_write_register(dev, frm0_addr, + scale_vdev->curr_buf->buff_addr[RKCIF_PLANE_Y]); + + if (!scale_vdev->next_buf) { + if (!list_empty(&scale_vdev->buf_head)) { + scale_vdev->next_buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, queue); + list_del(&scale_vdev->next_buf->queue); + } + } + + if (scale_vdev->next_buf) + rkcif_write_register(dev, frm1_addr, + scale_vdev->next_buf->buff_addr[RKCIF_PLANE_Y]); + + spin_unlock_irqrestore(&scale_vdev->vbq_lock, flags); +} + +static int rkcif_assign_scale_buffer_update(struct rkcif_scale_vdev *scale_vdev, + int channel_id) +{ + struct rkcif_device *dev = scale_vdev->cifdev; + struct rkcif_buffer *buffer = NULL; + u32 frm_addr; + int ret = 0; + unsigned long flags; + + frm_addr = scale_vdev->frame_phase & CIF_CSI_FRAME0_READY ? + get_reg_index_of_scale_frm0_addr(channel_id) : + get_reg_index_of_scale_frm1_addr(channel_id); + + spin_lock_irqsave(&scale_vdev->vbq_lock, flags); + if (!list_empty(&scale_vdev->buf_head)) { + if (scale_vdev->frame_phase == CIF_CSI_FRAME0_READY) { + scale_vdev->curr_buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, queue); + if (scale_vdev->curr_buf) { + list_del(&scale_vdev->curr_buf->queue); + buffer = scale_vdev->curr_buf; + } + } else if (scale_vdev->frame_phase == CIF_CSI_FRAME1_READY) { + scale_vdev->next_buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, queue); + if (scale_vdev->next_buf) { + list_del(&scale_vdev->next_buf->queue); + buffer = scale_vdev->next_buf; + } + } + } else { + buffer = NULL; + } + spin_unlock_irqrestore(&scale_vdev->vbq_lock, flags); + + if (buffer) { + rkcif_write_register(dev, frm_addr, + buffer->buff_addr[RKCIF_PLANE_Y]); + } else { + ret = -EINVAL; + v4l2_info(&dev->v4l2_dev, + "not active buffer,skip frame, scale ch[%d]\n", + scale_vdev->ch); + } + return ret; +} + +static int rkcif_assign_scale_buffer_pingpong(struct rkcif_scale_vdev *scale_vdev, + int init, int channel_id) +{ + int ret = 0; + + if (init) + rkcif_assign_scale_buffer_init(scale_vdev, channel_id); + else + ret = rkcif_assign_scale_buffer_update(scale_vdev, channel_id); + return ret; +} + +static int rkcif_scale_channel_set(struct rkcif_scale_vdev *scale_vdev) +{ + struct rkcif_device *dev = scale_vdev->cifdev; + u32 val = 0; + u32 ch = scale_vdev->ch; + + val = rkcif_read_register(dev, CIF_REG_SCL_CH_CTRL); + if (val & CIF_SCALE_EN(ch)) { + v4l2_err(&dev->v4l2_dev, "scale_vdev[%d] has been used by other device\n", ch); + return -EINVAL; + } + rkcif_write_register_and(dev, CIF_REG_GLB_INTST, + ~(SCALE_END_INTSTAT(ch) | + SCALE_FIFO_OVERFLOW(ch))); + rkcif_write_register_or(dev, CIF_REG_GLB_INTEN, + (SCALE_END_INTSTAT(ch) | + SCALE_FIFO_OVERFLOW(ch) | + SCALE_TOISP_AXI0_ERR | + SCALE_TOISP_AXI1_ERR)); + val = CIF_SCALE_SW_PRESS_ENABLE | + CIF_SCALE_SW_PRESS_VALUE(7) | + CIF_SCALE_SW_HURRY_ENABLE | + CIF_SCALE_SW_HURRY_VALUE(7) | + CIF_SCALE_SW_WATER_LINE(1); + + rkcif_write_register(dev, CIF_REG_SCL_CTRL, val); + val = scale_vdev->blc.pattern00 | + (scale_vdev->blc.pattern01 << 8) | + (scale_vdev->blc.pattern02 << 16) | + (scale_vdev->blc.pattern03 << 24); + rkcif_write_register(dev, CIF_REG_SCL_BLC_CH0 + ch, + val); + rkcif_write_register(dev, get_reg_index_of_scale_vlw(ch), + scale_vdev->ch_info.vir_width); + rkcif_assign_scale_buffer_pingpong(scale_vdev, + RKCIF_YUV_ADDR_STATE_INIT, + ch); + val = CIF_SCALE_SW_SRC_CH(scale_vdev->ch_src, ch) | + CIF_SCALE_SW_MODE(scale_vdev->scale_mode, ch) | + CIF_SCALE_EN(ch); + rkcif_write_register_or(dev, CIF_REG_SCL_CH_CTRL, + val); + return 0; +} + + +static int rkcif_scale_start(struct rkcif_scale_vdev *scale_vdev) +{ + int ret = 0; + struct rkcif_device *dev = scale_vdev->cifdev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + + mutex_lock(&dev->scale_lock); + if (scale_vdev->state == RKCIF_STATE_STREAMING) { + ret = -EBUSY; + v4l2_err(v4l2_dev, "stream in busy state\n"); + goto destroy_buf; + } + + rkcif_scale_channel_init(scale_vdev); + ret = rkcif_scale_channel_set(scale_vdev); + if (ret) + goto destroy_buf; + scale_vdev->frame_idx = 0; + scale_vdev->state = RKCIF_STATE_STREAMING; + mutex_unlock(&dev->scale_lock); + return 0; + +destroy_buf: + if (scale_vdev->next_buf) + vb2_buffer_done(&scale_vdev->next_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + if (scale_vdev->curr_buf) + vb2_buffer_done(&scale_vdev->curr_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + while (!list_empty(&scale_vdev->buf_head)) { + struct rkcif_buffer *buf; + + buf = list_first_entry(&scale_vdev->buf_head, + struct rkcif_buffer, queue); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + list_del(&buf->queue); + } + mutex_unlock(&dev->scale_lock); + return ret; +} + +static int +rkcif_scale_vb2_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct rkcif_scale_vdev *scale_vdev = queue->drv_priv; + struct rkcif_stream *stream = scale_vdev->stream; + int ret = 0; + + ret = rkcif_scale_start(scale_vdev); + if (ret) + return ret; + + rkcif_do_start_stream(stream, RKCIF_STREAM_MODE_TOSCALE); + return 0; +} + +static struct vb2_ops rkcif_scale_vb2_ops = { + .queue_setup = rkcif_scale_vb2_queue_setup, + .buf_queue = rkcif_scale_vb2_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = rkcif_scale_vb2_stop_streaming, + .start_streaming = rkcif_scale_vb2_start_streaming, +}; + +static int rkcif_scale_init_vb2_queue(struct vb2_queue *q, + struct rkcif_scale_vdev *scale_vdev, + enum v4l2_buf_type buf_type) +{ + struct rkcif_hw *hw_dev = scale_vdev->cifdev->hw_dev; + + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = scale_vdev; + q->ops = &rkcif_scale_vb2_ops; + if (hw_dev->iommu_en) + q->mem_ops = &vb2_dma_sg_memops; + else + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct rkcif_buffer); + q->min_buffers_needed = CIF_SCALE_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &scale_vdev->vnode.vlock; + q->dev = hw_dev->dev; + q->allow_cache_hints = 1; + q->bidirectional = 1; + q->gfp_flags = GFP_DMA32; + return vb2_queue_init(q); +} + + +static int rkcif_scale_g_ch(struct v4l2_device *v4l2_dev, + unsigned int intstat) +{ + if (intstat & SCALE_END_INTSTAT(0)) { + if ((intstat & SCALE_END_INTSTAT(0)) == + SCALE_END_INTSTAT(0)) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in CH0\n"); + return RKCIF_SCALE_CH0; + } + + if (intstat & SCALE_END_INTSTAT(1)) { + if ((intstat & SCALE_END_INTSTAT(1)) == + SCALE_END_INTSTAT(1)) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in CH1\n"); + return RKCIF_SCALE_CH1; + } + + if (intstat & SCALE_END_INTSTAT(2)) { + if ((intstat & SCALE_END_INTSTAT(2)) == + SCALE_END_INTSTAT(2)) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in CH2\n"); + return RKCIF_SCALE_CH2; + } + + if (intstat & SCALE_END_INTSTAT(3)) { + if ((intstat & SCALE_END_INTSTAT(3)) == + SCALE_END_INTSTAT(3)) + v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in CH3\n"); + return RKCIF_SCALE_CH3; + } + + return -EINVAL; +} + +static void rkcif_scale_vb_done_oneframe(struct rkcif_scale_vdev *scale_vdev, + struct vb2_v4l2_buffer *vb_done) +{ + const struct cif_output_fmt *fmt = scale_vdev->scale_out_fmt; + u32 i; + + /* Dequeue a filled buffer */ + for (i = 0; i < fmt->mplanes; i++) { + vb2_set_plane_payload(&vb_done->vb2_buf, i, + scale_vdev->pixm.plane_fmt[i].sizeimage); + } + + vb_done->vb2_buf.timestamp = ktime_get_ns(); + + vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE); +} + +static void rkcif_scale_update_stream(struct rkcif_scale_vdev *scale_vdev, int ch) +{ + struct rkcif_buffer *active_buf = NULL; + struct vb2_v4l2_buffer *vb_done = NULL; + int ret = 0; + + if (scale_vdev->frame_phase & CIF_CSI_FRAME0_READY) { + if (scale_vdev->curr_buf) + active_buf = scale_vdev->curr_buf; + } else if (scale_vdev->frame_phase & CIF_CSI_FRAME1_READY) { + if (scale_vdev->next_buf) + active_buf = scale_vdev->next_buf; + } + + ret = rkcif_assign_scale_buffer_pingpong(scale_vdev, + RKCIF_YUV_ADDR_STATE_UPDATE, + ch); + + if (active_buf && (!ret)) { + vb_done = &active_buf->vb; + vb_done->vb2_buf.timestamp = ktime_get_ns(); + vb_done->sequence = scale_vdev->frame_idx; + rkcif_scale_vb_done_oneframe(scale_vdev, vb_done); + } + scale_vdev->frame_idx++; +} + +void rkcif_irq_handle_scale(struct rkcif_device *cif_dev, unsigned int intstat_glb) +{ + struct rkcif_scale_vdev *scale_vdev; + int ch; + int i = 0; + u32 val = 0; + + val = SCALE_FIFO_OVERFLOW(0) | + SCALE_FIFO_OVERFLOW(1) | + SCALE_FIFO_OVERFLOW(2) | + SCALE_FIFO_OVERFLOW(3); + if (intstat_glb & val) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: scale channel, overflow intstat_glb:0x%x !!\n", + intstat_glb); + return; + } + + ch = rkcif_scale_g_ch(&cif_dev->v4l2_dev, + intstat_glb); + if (ch < 0) + return; + + for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { + ch = rkcif_scale_g_ch(&cif_dev->v4l2_dev, + intstat_glb); + if (ch < 0) + continue; + + scale_vdev = &cif_dev->scale_vdev[ch]; + + if (scale_vdev->state != RKCIF_STATE_STREAMING) + continue; + + if (scale_vdev->stopping) { + rkcif_scale_stop(scale_vdev); + scale_vdev->stopping = false; + wake_up(&scale_vdev->wq_stopped); + continue; + } + + scale_vdev->frame_phase = SW_SCALE_END(intstat_glb, ch); + intstat_glb &= ~(SCALE_END_INTSTAT(ch)); + rkcif_scale_update_stream(scale_vdev, ch); + } +} + +void rkcif_init_scale_vdev(struct rkcif_device *cif_dev, u32 ch) +{ + struct rkcif_scale_vdev *scale_vdev = &cif_dev->scale_vdev[ch]; + struct v4l2_pix_format_mplane pixm; + + memset(scale_vdev, 0, sizeof(*scale_vdev)); + memset(&pixm, 0, sizeof(pixm)); + scale_vdev->cifdev = cif_dev; + scale_vdev->stream = &cif_dev->stream[ch]; + scale_vdev->ch = ch; + scale_vdev->ch_src = 0; + scale_vdev->frame_idx = 0; + pixm.pixelformat = V4L2_PIX_FMT_SBGGR16; + pixm.width = RKCIF_DEFAULT_WIDTH; + pixm.height = RKCIF_DEFAULT_HEIGHT; + scale_vdev->state = RKCIF_STATE_READY; + scale_vdev->stopping = false; + scale_vdev->blc.pattern00 = 0; + scale_vdev->blc.pattern01 = 0; + scale_vdev->blc.pattern02 = 0; + scale_vdev->blc.pattern03 = 0; + INIT_LIST_HEAD(&scale_vdev->buf_head); + spin_lock_init(&scale_vdev->vbq_lock); + init_waitqueue_head(&scale_vdev->wq_stopped); + rkcif_scale_set_fmt(scale_vdev, &pixm, false); +} + +static int rkcif_register_scale_vdev(struct rkcif_scale_vdev *scale_vdev, bool is_multi_input) +{ + int ret = 0; + struct video_device *vdev = &scale_vdev->vnode.vdev; + struct rkcif_vdev_node *node; + char *vdev_name; + + switch (scale_vdev->ch) { + case RKCIF_SCALE_CH0: + vdev_name = CIF_SCALE_CH0_VDEV_NAME; + break; + case RKCIF_SCALE_CH1: + vdev_name = CIF_SCALE_CH1_VDEV_NAME; + break; + case RKCIF_SCALE_CH2: + vdev_name = CIF_SCALE_CH2_VDEV_NAME; + break; + case RKCIF_SCALE_CH3: + vdev_name = CIF_SCALE_CH3_VDEV_NAME; + break; + default: + ret = -EINVAL; + v4l2_err(&scale_vdev->cifdev->v4l2_dev, "Invalid stream\n"); + goto err_cleanup_media_entity; + } + + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + node = container_of(vdev, struct rkcif_vdev_node, vdev); + mutex_init(&node->vlock); + + vdev->ioctl_ops = &rkcif_scale_ioctl; + vdev->fops = &rkcif_scale_fops; + vdev->release = video_device_release_empty; + vdev->lock = &node->vlock; + vdev->v4l2_dev = &scale_vdev->cifdev->v4l2_dev; + vdev->queue = &node->buf_queue; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + node->pad.flags = MEDIA_PAD_FL_SINK; + video_set_drvdata(vdev, scale_vdev); + + rkcif_scale_init_vb2_queue(&node->buf_queue, + scale_vdev, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + vdev->queue = &node->buf_queue; + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto err_release_queue; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(&vdev->dev, + "could not register Video for Linux device\n"); + goto err_cleanup_media_entity; + } + return 0; + +err_cleanup_media_entity: + media_entity_cleanup(&vdev->entity); +err_release_queue: + vb2_queue_release(vdev->queue); + return ret; +} + +static void rkcif_unregister_scale_vdev(struct rkcif_scale_vdev *scale_vdev) +{ + struct rkcif_vdev_node *node = &scale_vdev->vnode; + struct video_device *vdev = &node->vdev; + + video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + vb2_queue_release(vdev->queue); +} + +int rkcif_register_scale_vdevs(struct rkcif_device *cif_dev, + int stream_num, + bool is_multi_input) +{ + struct rkcif_scale_vdev *scale_vdev; + int i, j, ret; + + for (i = 0; i < stream_num; i++) { + scale_vdev = &cif_dev->scale_vdev[i]; + ret = rkcif_register_scale_vdev(scale_vdev, is_multi_input); + if (ret < 0) + goto err; + } + + return 0; +err: + for (j = 0; j < i; j++) { + scale_vdev = &cif_dev->scale_vdev[j]; + rkcif_unregister_scale_vdev(scale_vdev); + } + + return ret; +} + +void rkcif_unregister_scale_vdevs(struct rkcif_device *cif_dev, + int stream_num) +{ + struct rkcif_scale_vdev *scale_vdev; + int i; + + for (i = 0; i < stream_num; i++) { + scale_vdev = &cif_dev->scale_vdev[i]; + rkcif_unregister_scale_vdev(scale_vdev); + } +} + diff --git a/drivers/media/platform/rockchip/cif/common.c b/drivers/media/platform/rockchip/cif/common.c new file mode 100644 index 000000000000..0650d4fc3b95 --- /dev/null +++ b/drivers/media/platform/rockchip/cif/common.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2021 Rockchip Electronics Co., Ltd */ + +#include +#include +#include +#include "dev.h" +#include "common.h" + +static int rkcif_alloc_buffer(struct rkcif_device *dev, + struct rkcif_dummy_buffer *buf) +{ + unsigned long attrs = buf->is_need_vaddr ? 0 : DMA_ATTR_NO_KERNEL_MAPPING; + const struct vb2_mem_ops *g_ops = dev->hw_dev->mem_ops; + struct sg_table *sg_tbl; + void *mem_priv; + int ret = 0; + + if (!buf->size) { + ret = -EINVAL; + goto err; + } + + if (dev->hw_dev->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->size = PAGE_ALIGN(buf->size); + mem_priv = g_ops->alloc(dev->hw_dev->dev, attrs, buf->size, + DMA_BIDIRECTIONAL, GFP_KERNEL | GFP_DMA32); + if (IS_ERR_OR_NULL(mem_priv)) { + ret = -ENOMEM; + goto err; + } + + buf->mem_priv = mem_priv; + if (dev->hw_dev->is_dma_sg_ops) { + sg_tbl = (struct sg_table *)g_ops->cookie(mem_priv); + buf->dma_addr = sg_dma_address(sg_tbl->sgl); + g_ops->prepare(mem_priv); + } else { + buf->dma_addr = *((dma_addr_t *)g_ops->cookie(mem_priv)); + } + if (buf->is_need_vaddr) + buf->vaddr = g_ops->vaddr(mem_priv); + if (buf->is_need_dbuf) { + buf->dbuf = g_ops->get_dmabuf(mem_priv, O_RDWR); + if (buf->is_need_dmafd) { + buf->dma_fd = dma_buf_fd(buf->dbuf, O_CLOEXEC); + if (buf->dma_fd < 0) { + dma_buf_put(buf->dbuf); + ret = buf->dma_fd; + goto err; + } + get_dma_buf(buf->dbuf); + } + } + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, + "%s buf:0x%x~0x%x size:%d\n", __func__, + (u32)buf->dma_addr, (u32)buf->dma_addr + buf->size, buf->size); + return ret; +err: + dev_err(dev->dev, "%s failed ret:%d\n", __func__, ret); + return ret; +} + +static void rkcif_free_buffer(struct rkcif_device *dev, + struct rkcif_dummy_buffer *buf) +{ + const struct vb2_mem_ops *g_ops = dev->hw_dev->mem_ops; + + if (buf && buf->mem_priv) { + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, + "%s buf:0x%x~0x%x\n", __func__, + (u32)buf->dma_addr, (u32)buf->dma_addr + buf->size); + if (buf->dbuf) + dma_buf_put(buf->dbuf); + g_ops->put(buf->mem_priv); + buf->size = 0; + buf->dbuf = NULL; + buf->vaddr = NULL; + buf->mem_priv = NULL; + buf->is_need_dbuf = false; + buf->is_need_vaddr = false; + buf->is_need_dmafd = false; + } +} + +static int rkcif_alloc_page_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf) +{ + struct rkcif_hw *hw = dev->hw_dev; + u32 i, n_pages = PAGE_ALIGN(buf->size) >> PAGE_SHIFT; + struct page *page = NULL, **pages = NULL; + struct sg_table *sg = NULL; + int ret = -ENOMEM; + + page = alloc_pages(GFP_KERNEL | GFP_DMA32, 0); + if (!page) + goto err; + + pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) + goto free_page; + for (i = 0; i < n_pages; i++) + pages[i] = page; + + sg = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!sg) + goto free_pages; + ret = sg_alloc_table_from_pages(sg, pages, n_pages, 0, + n_pages << PAGE_SHIFT, GFP_KERNEL); + if (ret) + goto free_sg; + + ret = dma_map_sg(hw->dev, sg->sgl, sg->nents, DMA_BIDIRECTIONAL); + buf->dma_addr = sg_dma_address(sg->sgl); + buf->mem_priv = sg; + buf->pages = pages; + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, + "%s buf:0x%x map cnt:%d size:%d\n", __func__, + (u32)buf->dma_addr, ret, buf->size); + return 0; +free_sg: + kfree(sg); +free_pages: + kvfree(pages); +free_page: + __free_pages(page, 0); +err: + return ret; +} + +static void rkcif_free_page_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf) +{ + struct sg_table *sg = buf->mem_priv; + + if (!sg) + return; + dma_unmap_sg(dev->hw_dev->dev, sg->sgl, sg->nents, DMA_BIDIRECTIONAL); + sg_free_table(sg); + kfree(sg); + __free_pages(buf->pages[0], 0); + kvfree(buf->pages); + buf->mem_priv = NULL; + buf->pages = NULL; +} + +int rkcif_alloc_common_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf) +{ + struct rkcif_hw *hw = dev->hw_dev; + int ret = 0; + + mutex_lock(&hw->dev_lock); + if (buf->mem_priv) + goto end; + + if (buf->size == 0) + goto end; + + if (hw->iommu_en) { + ret = rkcif_alloc_page_dummy_buf(dev, buf); + goto end; + } + + ret = rkcif_alloc_buffer(dev, buf); + if (!ret) + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, + "%s buf:0x%x size:%d\n", __func__, + (u32)buf->dma_addr, buf->size); +end: + if (ret < 0) + v4l2_err(&dev->v4l2_dev, "%s failed:%d\n", __func__, ret); + mutex_unlock(&hw->dev_lock); + return ret; +} + +void rkcif_free_common_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf) +{ + struct rkcif_hw *hw = dev->hw_dev; + + mutex_lock(&hw->dev_lock); + + if (hw->iommu_en) + rkcif_free_page_dummy_buf(dev, buf); + else + rkcif_free_buffer(dev, buf); + mutex_unlock(&hw->dev_lock); +} + diff --git a/drivers/media/platform/rockchip/cif/common.h b/drivers/media/platform/rockchip/cif/common.h new file mode 100644 index 000000000000..57b8eefd30db --- /dev/null +++ b/drivers/media/platform/rockchip/cif/common.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKCIF_COMMON_H +#define _RKCIF_COMMON_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dev.h" + +int rkcif_alloc_common_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf); +void rkcif_free_common_dummy_buf(struct rkcif_device *dev, struct rkcif_dummy_buffer *buf); + +#endif /* _RKCIF_COMMON_H */ + diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c index df8fb73c0a4c..9ec6005e7bc9 100644 --- a/drivers/media/platform/rockchip/cif/dev.c +++ b/drivers/media/platform/rockchip/cif/dev.c @@ -92,6 +92,9 @@ static ssize_t rkcif_store_compact_mode(struct device *dev, return len; } +static DEVICE_ATTR(compact_test, S_IWUSR | S_IRUSR, + rkcif_show_compact_mode, rkcif_store_compact_mode); + static ssize_t rkcif_show_line_int_num(struct device *dev, struct device_attribute *attr, char *buf) @@ -109,9 +112,15 @@ static ssize_t rkcif_store_line_int_num(struct device *dev, const char *buf, size_t len) { struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + struct sditf_priv *priv = cif_dev->sditf; int val = 0; int ret = 0; + if (priv->toisp_inf.link_mode != TOISP_NONE) { + dev_info(cif_dev->dev, + "current mode is on the fly, wake up mode wouldn't used\n"); + return len; + } ret = kstrtoint(buf, 0, &val); if (!ret && val >= 0 && val <= 0x3fff) cif_dev->wait_line_cache = val; @@ -120,6 +129,9 @@ static ssize_t rkcif_store_line_int_num(struct device *dev, return len; } +static DEVICE_ATTR(wait_line, S_IWUSR | S_IRUSR, + rkcif_show_line_int_num, rkcif_store_line_int_num); + static ssize_t rkcif_show_dummybuf_mode(struct device *dev, struct device_attribute *attr, char *buf) @@ -152,8 +164,11 @@ static ssize_t rkcif_store_dummybuf_mode(struct device *dev, return len; } -/* show the compact mode of each stream in stream index order, - * 1 for compact, 0 for 16bit +static DEVICE_ATTR(is_use_dummybuf, S_IWUSR | S_IRUSR, + rkcif_show_dummybuf_mode, rkcif_store_dummybuf_mode); + +/* show the memory mode of each stream in stream index order, + * 1 for high align, 0 for low align */ static ssize_t rkcif_show_memory_mode(struct device *dev, struct device_attribute *attr, @@ -210,24 +225,303 @@ static ssize_t rkcif_store_memory_mode(struct device *dev, return len; } -static DEVICE_ATTR(compact_test, S_IWUSR | S_IRUSR, - rkcif_show_compact_mode, rkcif_store_compact_mode); - -static DEVICE_ATTR(wait_line, S_IWUSR | S_IRUSR, - rkcif_show_line_int_num, rkcif_store_line_int_num); - -static DEVICE_ATTR(is_use_dummybuf, S_IWUSR | S_IRUSR, - rkcif_show_dummybuf_mode, rkcif_store_dummybuf_mode); - static DEVICE_ATTR(is_high_align, S_IWUSR | S_IRUSR, rkcif_show_memory_mode, rkcif_store_memory_mode); +static ssize_t rkcif_show_scale_ch0_blc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int ret; + + ret = snprintf(buf, PAGE_SIZE, "ch0 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[0].blc.pattern00, + cif_dev->scale_vdev[0].blc.pattern01, + cif_dev->scale_vdev[0].blc.pattern02, + cif_dev->scale_vdev[0].blc.pattern03); + return ret; +} + +static ssize_t rkcif_store_scale_ch0_blc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int i = 0, index = 0; + unsigned int val[4] = {0}; + unsigned int temp = 0; + int ret = 0; + int j = 0; + char cha[2] = {0}; + + if (buf) { + index = 0; + for (i = 0; i < len; i++) { + if (((buf[i] == ' ') || (buf[i] == '\n')) && j) { + index++; + j = 0; + if (index == 4) + break; + continue; + } else { + if (buf[i] < '0' || buf[i] > '9') + continue; + cha[0] = buf[i]; + cha[1] = '\0'; + ret = kstrtoint(cha, 0, &temp); + if (!ret) { + if (j) + val[index] *= 10; + val[index] += temp; + j++; + } + } + } + if (val[0] > 255 || val[1] > 255 || val[2] > 255 || val[3] > 255) + return -EINVAL; + cif_dev->scale_vdev[0].blc.pattern00 = val[0]; + cif_dev->scale_vdev[0].blc.pattern01 = val[1]; + cif_dev->scale_vdev[0].blc.pattern02 = val[2]; + cif_dev->scale_vdev[0].blc.pattern03 = val[3]; + dev_info(cif_dev->dev, + "set ch0 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[0].blc.pattern00, + cif_dev->scale_vdev[0].blc.pattern01, + cif_dev->scale_vdev[0].blc.pattern02, + cif_dev->scale_vdev[0].blc.pattern03); + } + + return len; +} + +static DEVICE_ATTR(scale_ch0_blc, S_IWUSR | S_IRUSR, + rkcif_show_scale_ch0_blc, rkcif_store_scale_ch0_blc); + +static ssize_t rkcif_show_scale_ch1_blc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int ret; + + ret = snprintf(buf, PAGE_SIZE, "ch1 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[1].blc.pattern00, + cif_dev->scale_vdev[1].blc.pattern01, + cif_dev->scale_vdev[1].blc.pattern02, + cif_dev->scale_vdev[1].blc.pattern03); + return ret; +} + +static ssize_t rkcif_store_scale_ch1_blc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int i = 0, index = 0; + unsigned int val[4] = {0}; + unsigned int temp = 0; + int ret = 0; + int j = 0; + char cha[2] = {0}; + + if (buf) { + index = 0; + for (i = 0; i < len; i++) { + if (((buf[i] == ' ') || (buf[i] == '\n')) && j) { + index++; + j = 0; + if (index == 4) + break; + continue; + } else { + if (buf[i] < '0' || buf[i] > '9') + continue; + cha[0] = buf[i]; + cha[1] = '\0'; + ret = kstrtoint(cha, 0, &temp); + if (!ret) { + if (j) + val[index] *= 10; + val[index] += temp; + j++; + } + } + } + if (val[0] > 255 || val[1] > 255 || val[2] > 255 || val[3] > 255) + return -EINVAL; + + cif_dev->scale_vdev[1].blc.pattern00 = val[0]; + cif_dev->scale_vdev[1].blc.pattern01 = val[1]; + cif_dev->scale_vdev[1].blc.pattern02 = val[2]; + cif_dev->scale_vdev[1].blc.pattern03 = val[3]; + + dev_info(cif_dev->dev, + "set ch1 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[1].blc.pattern00, + cif_dev->scale_vdev[1].blc.pattern01, + cif_dev->scale_vdev[1].blc.pattern02, + cif_dev->scale_vdev[1].blc.pattern03); + } + + return len; +} + +static DEVICE_ATTR(scale_ch1_blc, S_IWUSR | S_IRUSR, + rkcif_show_scale_ch1_blc, rkcif_store_scale_ch1_blc); + +static ssize_t rkcif_show_scale_ch2_blc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int ret; + + ret = snprintf(buf, PAGE_SIZE, "ch2 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[2].blc.pattern00, + cif_dev->scale_vdev[2].blc.pattern01, + cif_dev->scale_vdev[2].blc.pattern02, + cif_dev->scale_vdev[2].blc.pattern03); + return ret; +} + +static ssize_t rkcif_store_scale_ch2_blc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int i = 0, index = 0; + unsigned int val[4] = {0}; + unsigned int temp = 0; + int ret = 0; + int j = 0; + char cha[2] = {0}; + + if (buf) { + index = 0; + for (i = 0; i < len; i++) { + if (((buf[i] == ' ') || (buf[i] == '\n')) && j) { + index++; + j = 0; + if (index == 4) + break; + continue; + } else { + if (buf[i] < '0' || buf[i] > '9') + continue; + cha[0] = buf[i]; + cha[1] = '\0'; + ret = kstrtoint(cha, 0, &temp); + if (!ret) { + if (j) + val[index] *= 10; + val[index] += temp; + j++; + } + } + } + if (val[0] > 255 || val[1] > 255 || val[2] > 255 || val[3] > 255) + return -EINVAL; + + cif_dev->scale_vdev[2].blc.pattern00 = val[0]; + cif_dev->scale_vdev[2].blc.pattern01 = val[1]; + cif_dev->scale_vdev[2].blc.pattern02 = val[2]; + cif_dev->scale_vdev[2].blc.pattern03 = val[3]; + + dev_info(cif_dev->dev, + "set ch2 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[2].blc.pattern00, + cif_dev->scale_vdev[2].blc.pattern01, + cif_dev->scale_vdev[2].blc.pattern02, + cif_dev->scale_vdev[2].blc.pattern03); + } + + return len; +} +static DEVICE_ATTR(scale_ch2_blc, S_IWUSR | S_IRUSR, + rkcif_show_scale_ch2_blc, rkcif_store_scale_ch2_blc); + +static ssize_t rkcif_show_scale_ch3_blc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int ret; + + ret = snprintf(buf, PAGE_SIZE, "ch3 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[3].blc.pattern00, + cif_dev->scale_vdev[3].blc.pattern01, + cif_dev->scale_vdev[3].blc.pattern02, + cif_dev->scale_vdev[3].blc.pattern03); + return ret; +} + +static ssize_t rkcif_store_scale_ch3_blc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rkcif_device *cif_dev = (struct rkcif_device *)dev_get_drvdata(dev); + int i = 0, index = 0; + unsigned int val[4] = {0}; + unsigned int temp = 0; + int ret = 0; + int j = 0; + char cha[2] = {0}; + + if (buf) { + index = 0; + for (i = 0; i < len; i++) { + if (((buf[i] == ' ') || (buf[i] == '\n')) && j) { + index++; + j = 0; + if (index == 4) + break; + continue; + } else { + if (buf[i] < '0' || buf[i] > '9') + continue; + cha[0] = buf[i]; + cha[1] = '\0'; + ret = kstrtoint(cha, 0, &temp); + if (!ret) { + if (j) + val[index] *= 10; + val[index] += temp; + j++; + } + } + } + if (val[0] > 255 || val[1] > 255 || val[2] > 255 || val[3] > 255) + return -EINVAL; + + cif_dev->scale_vdev[3].blc.pattern00 = val[0]; + cif_dev->scale_vdev[3].blc.pattern01 = val[1]; + cif_dev->scale_vdev[3].blc.pattern02 = val[2]; + cif_dev->scale_vdev[3].blc.pattern03 = val[3]; + + dev_info(cif_dev->dev, + "set ch3 pattern00: %d, pattern01: %d, pattern02: %d, pattern03: %d\n", + cif_dev->scale_vdev[3].blc.pattern00, + cif_dev->scale_vdev[3].blc.pattern01, + cif_dev->scale_vdev[3].blc.pattern02, + cif_dev->scale_vdev[3].blc.pattern03); + } + + return len; +} + +static DEVICE_ATTR(scale_ch3_blc, S_IWUSR | S_IRUSR, + rkcif_show_scale_ch3_blc, rkcif_store_scale_ch3_blc); static struct attribute *dev_attrs[] = { &dev_attr_compact_test.attr, &dev_attr_wait_line.attr, &dev_attr_is_use_dummybuf.attr, &dev_attr_is_high_align.attr, + &dev_attr_scale_ch0_blc.attr, + &dev_attr_scale_ch1_blc.attr, + &dev_attr_scale_ch2_blc.attr, + &dev_attr_scale_ch3_blc.attr, NULL, }; @@ -244,11 +538,17 @@ void rkcif_write_register(struct rkcif_device *dev, { void __iomem *base = dev->hw_dev->base_addr; const struct cif_reg *reg = &dev->hw_dev->cif_regs[index]; + int csi_offset = 0; + if (dev->inf_id == RKCIF_MIPI_LVDS && + dev->chip_id == CHIP_RK3588_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = dev->csi_host_idx * 0x100; if (index < CIF_REG_INDEX_MAX) { if (index == CIF_REG_DVP_CTRL || (index != CIF_REG_DVP_CTRL && reg->offset != 0x0)) - write_cif_reg(base, reg->offset, val); + write_cif_reg(base, reg->offset + csi_offset, val); else v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "write reg[%d]:0x%x failed, maybe useless!!!\n", @@ -262,13 +562,20 @@ void rkcif_write_register_or(struct rkcif_device *dev, unsigned int reg_val = 0x0; void __iomem *base = dev->hw_dev->base_addr; const struct cif_reg *reg = &dev->hw_dev->cif_regs[index]; + int csi_offset = 0; + + if (dev->inf_id == RKCIF_MIPI_LVDS && + dev->chip_id == CHIP_RK3588_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = dev->csi_host_idx * 0x100; if (index < CIF_REG_INDEX_MAX) { if (index == CIF_REG_DVP_CTRL || (index != CIF_REG_DVP_CTRL && reg->offset != 0x0)) { - reg_val = read_cif_reg(base, reg->offset); + reg_val = read_cif_reg(base, reg->offset + csi_offset); reg_val |= val; - write_cif_reg(base, reg->offset, reg_val); + write_cif_reg(base, reg->offset + csi_offset, reg_val); } else { v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "write reg[%d]:0x%x with OR failed, maybe useless!!!\n", @@ -283,13 +590,20 @@ void rkcif_write_register_and(struct rkcif_device *dev, unsigned int reg_val = 0x0; void __iomem *base = dev->hw_dev->base_addr; const struct cif_reg *reg = &dev->hw_dev->cif_regs[index]; + int csi_offset = 0; + + if (dev->inf_id == RKCIF_MIPI_LVDS && + dev->chip_id == CHIP_RK3588_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = dev->csi_host_idx * 0x100; if (index < CIF_REG_INDEX_MAX) { if (index == CIF_REG_DVP_CTRL || (index != CIF_REG_DVP_CTRL && reg->offset != 0x0)) { - reg_val = read_cif_reg(base, reg->offset); + reg_val = read_cif_reg(base, reg->offset + csi_offset); reg_val &= val; - write_cif_reg(base, reg->offset, reg_val); + write_cif_reg(base, reg->offset + csi_offset, reg_val); } else { v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "write reg[%d]:0x%x with OR failed, maybe useless!!!\n", @@ -304,11 +618,18 @@ unsigned int rkcif_read_register(struct rkcif_device *dev, unsigned int val = 0x0; void __iomem *base = dev->hw_dev->base_addr; const struct cif_reg *reg = &dev->hw_dev->cif_regs[index]; + int csi_offset = 0; + + if (dev->inf_id == RKCIF_MIPI_LVDS && + dev->chip_id == CHIP_RK3588_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = dev->csi_host_idx * 0x100; if (index < CIF_REG_INDEX_MAX) { if (index == CIF_REG_DVP_CTRL || (index != CIF_REG_DVP_CTRL && reg->offset != 0x0)) - val = read_cif_reg(base, reg->offset); + val = read_cif_reg(base, reg->offset + csi_offset); else v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "read reg[%d] failed, maybe useless!!!\n", @@ -375,6 +696,12 @@ void rkcif_enable_dvp_clk_dual_edge(struct rkcif_device *dev, bool on) else val = CIF_SAMPLING_EDGE_SINGLE; rkcif_write_grf_reg(dev, CIF_REG_GRF_CIFIO_CON, val); + } else if (dev->chip_id == CHIP_RK3588_CIF) { + if (on) + val = RK3588_CIF_PCLK_DUAL_EDGE; + else + val = RK3588_CIF_PCLK_SINGLE_EDGE; + rkcif_write_grf_reg(dev, CIF_REG_GRF_CIFIO_CON, val); } } @@ -402,6 +729,13 @@ void rkcif_config_dvp_clk_sampling_edge(struct rkcif_device *dev, else val = RK3568_CIF_PCLK_SAMPLING_EDGE_FALLING; } + + if (dev->chip_id == CHIP_RK3588_CIF) { + if (edge == RKCIF_CLK_RISING) + val = RK3588_CIF_PCLK_SAMPLING_EDGE_RISING; + else + val = RK3588_CIF_PCLK_SAMPLING_EDGE_FALLING; + } rkcif_write_grf_reg(dev, CIF_REG_GRF_CIFIO_CON, val); } } @@ -490,7 +824,7 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) bool can_be_set = false; int i, ret; - if (cif_dev->hdr.mode == NO_HDR) { + if (cif_dev->hdr.hdr_mode == NO_HDR) { if ((on && atomic_inc_return(&p->stream_cnt) > 1) || (!on && atomic_dec_return(&p->stream_cnt) > 0)) return 0; @@ -504,6 +838,9 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) cif_dev->irq_stats.dvp_overflow_cnt = 0; cif_dev->irq_stats.dvp_pix_err_cnt = 0; cif_dev->irq_stats.all_err_cnt = 0; + cif_dev->irq_stats.csi_size_err_cnt = 0; + cif_dev->irq_stats.dvp_size_err_cnt = 0; + cif_dev->irq_stats.dvp_bwidth_lack_cnt = 0; cif_dev->irq_stats.all_frm_end_cnt = 0; cif_dev->reset_watchdog_timer.is_triggered = false; cif_dev->reset_watchdog_timer.is_running = false; @@ -524,14 +861,14 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) if (on) { atomic_inc(&p->stream_cnt); - if (cif_dev->hdr.mode == HDR_X2) { + if (cif_dev->hdr.hdr_mode == HDR_X2) { if (atomic_read(&p->stream_cnt) == 1) { rockchip_set_system_status(SYS_STATUS_CIF0); can_be_set = false; } else if (atomic_read(&p->stream_cnt) == 2) { can_be_set = true; } - } else if (cif_dev->hdr.mode == HDR_X3) { + } else if (cif_dev->hdr.hdr_mode == HDR_X3) { if (atomic_read(&p->stream_cnt) == 1) { rockchip_set_system_status(SYS_STATUS_CIF0); can_be_set = false; @@ -549,7 +886,10 @@ static int rkcif_pipeline_set_stream(struct rkcif_pipeline *p, bool on) cif_dev->irq_stats.dvp_line_err_cnt = 0; cif_dev->irq_stats.dvp_overflow_cnt = 0; cif_dev->irq_stats.dvp_pix_err_cnt = 0; + cif_dev->irq_stats.dvp_bwidth_lack_cnt = 0; cif_dev->irq_stats.all_err_cnt = 0; + cif_dev->irq_stats.csi_size_err_cnt = 0; + cif_dev->irq_stats.dvp_size_err_cnt = 0; cif_dev->irq_stats.all_frm_end_cnt = 0; cif_dev->is_start_hdr = true; cif_dev->reset_watchdog_timer.is_triggered = false; @@ -581,98 +921,104 @@ err_stream_off: return ret; } -/***************************** media controller *******************************/ -static int rkcif_create_links(struct rkcif_device *dev) +static int rkcif_create_link(struct rkcif_device *dev, + struct rkcif_sensor_info *sensor, + u32 stream_num, + bool *mipi_lvds_linked) { - int ret; - u32 flags; - unsigned int s, pad, id, stream_num = 0; - bool mipi_lvds_linked = false; + struct rkcif_sensor_info linked_sensor; + struct media_entity *source_entity, *sink_entity; + int ret = 0; + u32 flags, pad, id; - if (dev->chip_id < CHIP_RV1126_CIF) { - if (dev->inf_id == RKCIF_MIPI_LVDS) - stream_num = RKCIF_MAX_STREAM_MIPI; - else - stream_num = RKCIF_SINGLE_STREAM; + linked_sensor.lanes = sensor->lanes; + + if (sensor->mbus.type == V4L2_MBUS_CCP2) { + linked_sensor.sd = &dev->lvds_subdev.sd; + dev->lvds_subdev.sensor_self.sd = &dev->lvds_subdev.sd; + dev->lvds_subdev.sensor_self.lanes = sensor->lanes; + memcpy(&dev->lvds_subdev.sensor_self.mbus, &sensor->mbus, + sizeof(struct v4l2_mbus_config)); } else { - stream_num = RKCIF_MAX_STREAM_MIPI; + linked_sensor.sd = sensor->sd; } - /* sensor links(or mipi-phy) */ - for (s = 0; s < dev->num_sensors; ++s) { - struct rkcif_sensor_info *sensor = &dev->sensors[s]; - struct rkcif_sensor_info linked_sensor; - struct media_entity *source_entity, *sink_entity; + memcpy(&linked_sensor.mbus, &sensor->mbus, + sizeof(struct v4l2_mbus_config)); - linked_sensor.lanes = sensor->lanes; + for (pad = 0; pad < linked_sensor.sd->entity.num_pads; pad++) { + if (linked_sensor.sd->entity.pads[pad].flags & + MEDIA_PAD_FL_SOURCE) { + if (pad == linked_sensor.sd->entity.num_pads) { + dev_err(dev->dev, + "failed to find src pad for %s\n", + linked_sensor.sd->name); - if (sensor->mbus.type == V4L2_MBUS_CCP2) { - linked_sensor.sd = &dev->lvds_subdev.sd; - dev->lvds_subdev.sensor_self.sd = &dev->lvds_subdev.sd; - dev->lvds_subdev.sensor_self.lanes = sensor->lanes; - memcpy(&dev->lvds_subdev.sensor_self.mbus, &sensor->mbus, - sizeof(struct v4l2_mbus_config)); - } else { - linked_sensor.sd = sensor->sd; - } + break; + } - memcpy(&linked_sensor.mbus, &sensor->mbus, - sizeof(struct v4l2_mbus_config)); + if ((linked_sensor.mbus.type == V4L2_MBUS_BT656 || + linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) && + (dev->chip_id == CHIP_RK1808_CIF)) { + source_entity = &linked_sensor.sd->entity; + sink_entity = &dev->stream[RKCIF_STREAM_CIF].vnode.vdev.entity; - for (pad = 0; pad < linked_sensor.sd->entity.num_pads; pad++) { - if (linked_sensor.sd->entity.pads[pad].flags & - MEDIA_PAD_FL_SOURCE) { - if (pad == linked_sensor.sd->entity.num_pads) { - dev_err(dev->dev, - "failed to find src pad for %s\n", + ret = media_create_pad_link(source_entity, + pad, + sink_entity, + 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(dev->dev, "failed to create link for %s\n", linked_sensor.sd->name); + break; + } + if ((linked_sensor.mbus.type == V4L2_MBUS_BT656 || + linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) && + (dev->chip_id >= CHIP_RV1126_CIF)) { + source_entity = &linked_sensor.sd->entity; + sink_entity = &dev->stream[pad].vnode.vdev.entity; + + ret = media_create_pad_link(source_entity, + pad, + sink_entity, + 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(dev->dev, "failed to create link for %s pad[%d]\n", + linked_sensor.sd->name, pad); + continue; + } + + for (id = 0; id < stream_num; id++) { + source_entity = &linked_sensor.sd->entity; + sink_entity = &dev->stream[id].vnode.vdev.entity; + + if ((dev->chip_id < CHIP_RK1808_CIF) || + (id == pad - 1 && !(*mipi_lvds_linked))) + flags = MEDIA_LNK_FL_ENABLED; + else + flags = 0; + + ret = media_create_pad_link(source_entity, + pad, + sink_entity, + 0, + flags); + if (ret) { + dev_err(dev->dev, + "failed to create link for %s\n", + linked_sensor.sd->name); break; } - - if ((linked_sensor.mbus.type == V4L2_MBUS_BT656 || - linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) && - (dev->chip_id == CHIP_RK1808_CIF)) { - source_entity = &linked_sensor.sd->entity; - sink_entity = &dev->stream[RKCIF_STREAM_CIF].vnode.vdev.entity; - - ret = media_create_pad_link(source_entity, - pad, - sink_entity, - 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - dev_err(dev->dev, "failed to create link for %s\n", - linked_sensor.sd->name); - break; - } - - if ((linked_sensor.mbus.type == V4L2_MBUS_BT656 || - linked_sensor.mbus.type == V4L2_MBUS_PARALLEL) && - (dev->chip_id >= CHIP_RV1126_CIF)) { - source_entity = &linked_sensor.sd->entity; - sink_entity = &dev->stream[pad].vnode.vdev.entity; - - ret = media_create_pad_link(source_entity, - pad, - sink_entity, - 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - dev_err(dev->dev, "failed to create link for %s pad[%d]\n", - linked_sensor.sd->name, pad); - continue; - } - + } + if (dev->chip_id == CHIP_RK3588_CIF) { for (id = 0; id < stream_num; id++) { source_entity = &linked_sensor.sd->entity; - sink_entity = &dev->stream[id].vnode.vdev.entity; + sink_entity = &dev->scale_vdev[id].vnode.vdev.entity; - if ((dev->chip_id != CHIP_RK1808_CIF && - dev->chip_id != CHIP_RV1126_CIF && - dev->chip_id != CHIP_RV1126_CIF_LITE && - dev->chip_id != CHIP_RK3568_CIF) || - (id == pad - 1 && !mipi_lvds_linked)) + if ((id + stream_num) == pad - 1 && !(*mipi_lvds_linked)) flags = MEDIA_LNK_FL_ENABLED; else flags = 0; @@ -691,24 +1037,49 @@ static int rkcif_create_links(struct rkcif_device *dev) } } } + } - if (sensor->mbus.type == V4L2_MBUS_CCP2) { - source_entity = &sensor->sd->entity; - sink_entity = &linked_sensor.sd->entity; - ret = media_create_pad_link(source_entity, - 1, - sink_entity, - 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - dev_err(dev->dev, "failed to create link between %s and %s\n", - linked_sensor.sd->name, - sensor->sd->name); - } + if (sensor->mbus.type == V4L2_MBUS_CCP2) { + source_entity = &sensor->sd->entity; + sink_entity = &linked_sensor.sd->entity; + ret = media_create_pad_link(source_entity, + 1, + sink_entity, + 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(dev->dev, "failed to create link between %s and %s\n", + linked_sensor.sd->name, + sensor->sd->name); + } - if (linked_sensor.mbus.type != V4L2_MBUS_BT656 && - linked_sensor.mbus.type != V4L2_MBUS_PARALLEL) - mipi_lvds_linked = true; + if (linked_sensor.mbus.type != V4L2_MBUS_BT656 && + linked_sensor.mbus.type != V4L2_MBUS_PARALLEL) + *mipi_lvds_linked = true; + return ret; +} + +/***************************** media controller *******************************/ +static int rkcif_create_links(struct rkcif_device *dev) +{ + u32 s = 0; + u32 stream_num = 0; + bool mipi_lvds_linked = false; + + if (dev->chip_id < CHIP_RV1126_CIF) { + if (dev->inf_id == RKCIF_MIPI_LVDS) + stream_num = RKCIF_MAX_STREAM_MIPI; + else + stream_num = RKCIF_SINGLE_STREAM; + } else { + stream_num = RKCIF_MAX_STREAM_MIPI; + } + + /* sensor links(or mipi-phy) */ + for (s = 0; s < dev->num_sensors; ++s) { + struct rkcif_sensor_info *sensor = &dev->sensors[s]; + + rkcif_create_link(dev, sensor, stream_num, &mipi_lvds_linked); } return 0; @@ -762,7 +1133,8 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) } if (sensor->mbus.type == V4L2_MBUS_CCP2 || - sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) { + sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + sensor->mbus.type == V4L2_MBUS_CSI2_CPHY) { switch (sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) { case V4L2_MBUS_CSI2_1_LANE: @@ -870,12 +1242,14 @@ static int rkcif_fwnode_parse(struct device *dev, if (vep->bus_type != V4L2_MBUS_BT656 && vep->bus_type != V4L2_MBUS_PARALLEL && vep->bus_type != V4L2_MBUS_CSI2_DPHY && + vep->bus_type != V4L2_MBUS_CSI2_CPHY && vep->bus_type != V4L2_MBUS_CCP2) return 0; rk_asd->mbus.type = vep->bus_type; - if (vep->bus_type == V4L2_MBUS_CSI2_DPHY) { + if (vep->bus_type == V4L2_MBUS_CSI2_DPHY || + vep->bus_type == V4L2_MBUS_CSI2_CPHY) { rk_asd->mbus.flags = vep->bus.mipi_csi2.flags; rk_asd->lanes = vep->bus.mipi_csi2.num_data_lanes; } else if (vep->bus_type == V4L2_MBUS_CCP2) { @@ -942,6 +1316,14 @@ static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev) return -EINVAL; } + if (cif_dev->chip_id == CHIP_RK3588_CIF) { + ret = rkcif_register_scale_vdevs(cif_dev, RKCIF_MAX_SCALE_CH, true); + + if (ret < 0) { + dev_err(cif_dev->dev, "cif register scale_vdev[%d] failed!\n", stream_num); + goto err_unreg_stream_vdev; + } + } ret = cif_subdev_notifier(cif_dev); if (ret < 0) { v4l2_err(&cif_dev->v4l2_dev, @@ -952,17 +1334,22 @@ static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev) return 0; err_unreg_stream_vdev: rkcif_unregister_stream_vdevs(cif_dev, stream_num); + if (cif_dev->chip_id == CHIP_RK3588_CIF) + rkcif_unregister_scale_vdevs(cif_dev, RKCIF_MAX_SCALE_CH); return ret; } static irqreturn_t rkcif_irq_handler(int irq, struct rkcif_device *cif_dev) { - if (cif_dev->workmode == RKCIF_WORKMODE_PINGPONG) - rkcif_irq_pingpong(cif_dev); - else + if (cif_dev->workmode == RKCIF_WORKMODE_PINGPONG) { + if (cif_dev->chip_id < CHIP_RK3588_CIF) + rkcif_irq_pingpong(cif_dev); + else + rkcif_irq_pingpong_v1(cif_dev); + } else { rkcif_irq_oneframe(cif_dev); - + } return IRQ_HANDLED; } @@ -1149,10 +1536,11 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int struct v4l2_device *v4l2_dev; int ret; - cif_dev->hdr.mode = NO_HDR; + cif_dev->hdr.hdr_mode = NO_HDR; cif_dev->inf_id = inf_id; mutex_init(&cif_dev->stream_lock); + mutex_init(&cif_dev->scale_lock); spin_lock_init(&cif_dev->hdr_lock); spin_lock_init(&cif_dev->reset_watchdog_timer.timer_lock); spin_lock_init(&cif_dev->reset_watchdog_timer.csi2_err_lock); @@ -1164,6 +1552,7 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int cif_dev->pipe.close = rkcif_pipeline_close; cif_dev->pipe.set_stream = rkcif_pipeline_set_stream; cif_dev->isr_hdl = rkcif_irq_handler; + cif_dev->id_use_cnt = 0; if (cif_dev->chip_id == CHIP_RV1126_CIF_LITE) cif_dev->isr_hdl = rkcif_irq_lite_handler; @@ -1184,6 +1573,13 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID3); } + if (cif_dev->chip_id == CHIP_RK3588_CIF) { + rkcif_init_scale_vdev(cif_dev, RKCIF_SCALE_CH0); + rkcif_init_scale_vdev(cif_dev, RKCIF_SCALE_CH1); + rkcif_init_scale_vdev(cif_dev, RKCIF_SCALE_CH2); + rkcif_init_scale_vdev(cif_dev, RKCIF_SCALE_CH3); + } + #if defined(CONFIG_ROCKCHIP_CIF_WORKMODE_PINGPONG) cif_dev->workmode = RKCIF_WORKMODE_PINGPONG; #elif defined(CONFIG_ROCKCHIP_CIF_WORKMODE_ONEFRAME) @@ -1200,6 +1596,9 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int strlcpy(cif_dev->media_dev.model, dev_name(dev), sizeof(cif_dev->media_dev.model)); + cif_dev->csi_host_idx = of_alias_get_id(node, "rkcif_mipi_lvds"); + if (cif_dev->csi_host_idx < 0 || cif_dev->csi_host_idx > 5) + cif_dev->csi_host_idx = 0; cif_dev->media_dev.dev = dev; v4l2_dev = &cif_dev->v4l2_dev; v4l2_dev->mdev = &cif_dev->media_dev; @@ -1328,6 +1727,9 @@ static int rkcif_plat_probe(struct platform_device *pdev) dev_set_drvdata(dev, cif_dev); cif_dev->dev = dev; + if (sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp)) + return -ENODEV; + rkcif_attach_hw(cif_dev); rkcif_parse_dts(cif_dev); @@ -1338,9 +1740,6 @@ static int rkcif_plat_probe(struct platform_device *pdev) return ret; } - if (sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp)) - return -ENODEV; - if (rkcif_proc_init(cif_dev)) dev_warn(dev, "dev:%s create proc failed\n", dev_name(dev)); diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index d8d65675c3da..6b2f66040272 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "regs.h" #include "version.h" @@ -46,6 +47,9 @@ #define CIF_DVP_ID2_VDEV_NAME CIF_VIDEODEVICE_NAME "_dvp_id2" #define CIF_DVP_ID3_VDEV_NAME CIF_VIDEODEVICE_NAME "_dvp_id3" +#define RKCIF_PLANE_Y 0 +#define RKCIF_PLANE_CBCR 1 + /* * RK1808 support 5 channel inputs simultaneously: * dvp + 4 mipi virtual channels; @@ -69,10 +73,13 @@ #define RKCIF_DEFAULT_HEIGHT 480 #define RKCIF_FS_DETECTED_NUM 2 +#define RKCIF_RX_BUF_MAX 8 + /* * for HDR mode sync buf */ #define RDBK_MAX 3 +#define RDBK_TOISP_MAX 2 #define RDBK_L 0 #define RDBK_M 1 #define RDBK_S 2 @@ -89,6 +96,13 @@ enum rkcif_workmode { RKCIF_WORKMODE_LINELOOP = 0x02 }; +enum rkcif_stream_mode { + RKCIF_STREAM_MODE_NONE = 0x0, + RKCIF_STREAM_MODE_CAPTURE = 0x01, + RKCIF_STREAM_MODE_TOISP = 0x02, + RKCIF_STREAM_MODE_TOSCALE = 0x04 +}; + enum rkcif_yuvaddr_state { RKCIF_YUV_ADDR_STATE_UPDATE = 0x0, RKCIF_YUV_ADDR_STATE_INIT = 0x1 @@ -171,9 +185,17 @@ struct rkcif_buffer { }; struct rkcif_dummy_buffer { - void *vaddr; + struct list_head list; + struct dma_buf *dbuf; dma_addr_t dma_addr; + struct page **pages; + void *mem_priv; + void *vaddr; u32 size; + int dma_fd; + bool is_need_vaddr; + bool is_need_dbuf; + bool is_need_dmafd; }; extern int rkcif_debug; @@ -322,10 +344,13 @@ struct rkcif_readout_stats { struct rkcif_irq_stats { u64 csi_overflow_cnt; u64 csi_bwidth_lack_cnt; + u64 csi_size_err_cnt; u64 dvp_bus_err_cnt; u64 dvp_overflow_cnt; u64 dvp_line_err_cnt; u64 dvp_pix_err_cnt; + u64 dvp_size_err_cnt; + u64 dvp_bwidth_lack_cnt; u64 all_frm_end_cnt; u64 all_err_cnt; }; @@ -394,6 +419,22 @@ struct rkcif_extend_info { bool is_extended; }; +enum rkcif_capture_mode { + RKCIF_TO_DDR = 0, + RKCIF_TO_ISP_DDR, + RKCIF_TO_ISP_DMA, +}; + +struct rkcif_rx_buffer { + struct rkisp_rx_buf dbufs; + struct rkcif_dummy_buffer dummy; +}; + +enum rkcif_dma_en_mode { + RKCIF_DMAEN_BY_VICAP = 0x1, + RKCIF_DMAEN_BY_ISP = 0x2, +}; + /* * struct rkcif_stream - Stream states TODO * @@ -421,6 +462,8 @@ struct rkcif_stream { struct list_head buf_head; struct rkcif_buffer *curr_buf; struct rkcif_buffer *next_buf; + struct rkcif_rx_buffer *curr_buf_toisp; + struct rkcif_rx_buffer *next_buf_toisp; spinlock_t vbq_lock; /* vfd lock */ spinlock_t fps_lock; @@ -435,6 +478,15 @@ struct rkcif_stream { struct rkcif_extend_info extend_line; struct rkcif_readout_stats readout; unsigned int fs_cnt_in_single_frame; + unsigned int capture_mode; + struct rkcif_scale_vdev *scale_vdev; + int dma_en; + int to_en_dma; + int to_stop_dma; + unsigned int cur_stream_mode; + struct rkcif_rx_buffer rx_buf[RKCIF_RX_BUF_MAX]; + struct list_head rx_buf_head; + int buf_num_toisp; u64 line_int_cnt; int vc; bool stopping; @@ -499,6 +551,110 @@ static inline struct vb2_queue *to_vb2_queue(struct file *file) return &vnode->buf_queue; } +#define SCALE_DRIVER_NAME "rkcif_scale" + +#define RKCIF_SCALE_CH0 0 +#define RKCIF_SCALE_CH1 1 +#define RKCIF_SCALE_CH2 2 +#define RKCIF_SCALE_CH3 3 +#define RKCIF_MAX_SCALE_CH 4 + +#define CIF_SCALE_CH0_VDEV_NAME CIF_DRIVER_NAME "_scale_ch0" +#define CIF_SCALE_CH1_VDEV_NAME CIF_DRIVER_NAME "_scale_ch1" +#define CIF_SCALE_CH2_VDEV_NAME CIF_DRIVER_NAME "_scale_ch2" +#define CIF_SCALE_CH3_VDEV_NAME CIF_DRIVER_NAME "_scale_ch3" + +#define RKCIF_SCALE_ENUM_SIZE_MAX 3 + +enum scale_ch_sw { + SCALE_MIPI0_ID0, + SCALE_MIPI0_ID1, + SCALE_MIPI0_ID2, + SCALE_MIPI0_ID3, + SCALE_MIPI1_ID0, + SCALE_MIPI1_ID1, + SCALE_MIPI1_ID2, + SCALE_MIPI1_ID3, + SCALE_MIPI2_ID0, + SCALE_MIPI2_ID1, + SCALE_MIPI2_ID2, + SCALE_MIPI2_ID3, + SCALE_MIPI3_ID0, + SCALE_MIPI3_ID1, + SCALE_MIPI3_ID2, + SCALE_MIPI3_ID3, + SCALE_MIPI4_ID0, + SCALE_MIPI4_ID1, + SCALE_MIPI4_ID2, + SCALE_MIPI4_ID3, + SCALE_MIPI5_ID0, + SCALE_MIPI5_ID1, + SCALE_MIPI5_ID2, + SCALE_MIPI5_ID3, + SCALE_DVP, + SCALE_CH_MAX, +}; + +enum scale_mode { + SCALE_8TIMES, + SCALE_16TIMES, + SCALE_32TIMES, +}; + +struct rkcif_scale_ch_info { + u32 width; + u32 height; + u32 vir_width; +}; + +struct rkcif_scale_src_res { + u32 width; + u32 height; +}; + +/* + * struct rkcif_scale_vdev - CIF Capture device + * + * @irq_lock: buffer queue lock + * @stat: stats buffer list + * @readout_wq: workqueue for statistics information read + */ +struct rkcif_scale_vdev { + unsigned int ch:3; + struct rkcif_device *cifdev; + struct rkcif_vdev_node vnode; + struct rkcif_stream *stream; + struct list_head buf_head; + spinlock_t vbq_lock; /* vfd lock */ + wait_queue_head_t wq_stopped; + struct v4l2_pix_format_mplane pixm; + const struct cif_output_fmt *scale_out_fmt; + struct rkcif_scale_ch_info ch_info; + struct rkcif_scale_src_res src_res; + struct rkcif_buffer *curr_buf; + struct rkcif_buffer *next_buf; + struct bayer_blc blc; + enum rkcif_state state; + unsigned int ch_src; + unsigned int scale_mode; + int frame_phase; + unsigned int frame_idx; + bool stopping; +}; + +static inline +struct rkcif_scale_vdev *to_rkcif_scale_vdev(struct rkcif_vdev_node *vnode) +{ + return container_of(vnode, struct rkcif_scale_vdev, vnode); +} + +void rkcif_init_scale_vdev(struct rkcif_device *cif_dev, u32 ch); +int rkcif_register_scale_vdevs(struct rkcif_device *cif_dev, + int stream_num, + bool is_multi_input); +void rkcif_unregister_scale_vdevs(struct rkcif_device *cif_dev, + int stream_num); + /* * struct rkcif_device - ISP platform device * @base_addr: base register address @@ -518,6 +674,7 @@ struct rkcif_device { struct rkcif_sensor_info terminal_sensor; struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM]; + struct rkcif_scale_vdev scale_vdev[RKCIF_MULTI_STREAMS_NUM]; struct rkcif_pipeline pipe; struct csi_channel_info channels[RKCIF_MAX_CSI_CHANNEL]; @@ -526,9 +683,10 @@ struct rkcif_device { atomic_t stream_cnt; atomic_t fh_cnt; struct mutex stream_lock; /* lock between streams */ + struct mutex scale_lock; /* lock between scale dev */ enum rkcif_workmode workmode; bool can_be_reset; - struct rkcif_hdr hdr; + struct rkmodule_hdr_cfg hdr; struct rkcif_buffer *rdbk_buf[RDBK_MAX]; struct rkcif_luma_vdev luma_vdev; struct rkcif_lvds_subdev lvds_subdev; @@ -545,6 +703,8 @@ struct rkcif_device { unsigned int buf_wake_up_cnt; struct notifier_block reset_notifier; /* reset for mipi csi crc err */ struct rkcif_work_struct reset_work; + int id_use_cnt; + unsigned int csi_host_idx; unsigned int dvp_sof_in_oneframe; unsigned int wait_line; unsigned int wait_line_bak; @@ -557,6 +717,17 @@ struct rkcif_device { }; extern struct platform_driver rkcif_plat_drv; +int rkcif_do_start_stream(struct rkcif_stream *stream, + enum rkcif_stream_mode mode); +void rkcif_do_stop_stream(struct rkcif_stream *stream, + enum rkcif_stream_mode mode); +void rkcif_irq_handle_scale(struct rkcif_device *cif_dev, + unsigned int intstat_glb); + +const struct +cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, + struct v4l2_rect *rect, + u32 pad_id, int *vc); void rkcif_write_register(struct rkcif_device *dev, enum cif_reg_index index, u32 val); @@ -579,6 +750,9 @@ void rkcif_stream_init(struct rkcif_device *dev, u32 id); void rkcif_set_default_fmt(struct rkcif_device *cif_dev); void rkcif_irq_oneframe(struct rkcif_device *cif_dev); void rkcif_irq_pingpong(struct rkcif_device *cif_dev); +void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev); +unsigned int rkcif_irq_global(struct rkcif_device *cif_dev); +void rkcif_irq_handle_toisp(struct rkcif_device *cif_dev, unsigned int intstat_glb); void rkcif_soft_reset(struct rkcif_device *cif_dev, bool is_rst_iommu); int rkcif_register_lvds_subdev(struct rkcif_device *dev); @@ -597,4 +771,16 @@ void rkcif_config_dvp_clk_sampling_edge(struct rkcif_device *dev, enum rkcif_clk_edge edge); void rkcif_enable_dvp_clk_dual_edge(struct rkcif_device *dev, bool on); void rkcif_reset_work(struct work_struct *work); + +int rkcif_init_rx_buf(struct rkcif_stream *stream, int buf_num); +void rkcif_free_rx_buf(struct rkcif_stream *stream, int buf_num); + +void rkcif_set_fmt(struct rkcif_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try); + +u32 rkcif_mbus_pixelcode_to_v4l2(u32 pixelcode); + +extern const struct vb2_mem_ops vb2_rdma_sg_memops; + #endif diff --git a/drivers/media/platform/rockchip/cif/hw.c b/drivers/media/platform/rockchip/cif/hw.c index da3032c68c13..f77f98070a31 100644 --- a/drivers/media/platform/rockchip/cif/hw.c +++ b/drivers/media/platform/rockchip/cif/hw.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include "dev.h" +#include "common.h" static const struct cif_reg px30_cif_regs[] = { [CIF_REG_DVP_CTRL] = CIF_REG(CIF_CTRL), @@ -597,6 +599,128 @@ static const struct cif_reg rk3568_cif_regs[] = { [CIF_REG_GRF_CIFIO_CON1] = CIF_REG(CIF_GRF_VI_CON1), }; +static const char * const rk3588_cif_clks[] = { + "aclk_cif", + "hclk_cif", + "dclk_cif", +}; + +static const char * const rk3588_cif_rsts[] = { + "rst_cif_a", + "rst_cif_h", + "rst_cif_d", +}; + +static const struct cif_reg rk3588_cif_regs[] = { + [CIF_REG_DVP_CTRL] = CIF_REG(DVP_CTRL), + [CIF_REG_DVP_INTEN] = CIF_REG(DVP_INTEN), + [CIF_REG_DVP_INTSTAT] = CIF_REG(DVP_INTSTAT), + [CIF_REG_DVP_FOR] = CIF_REG(DVP_FOR), + [CIF_REG_DVP_MULTI_ID] = CIF_REG(DVP_MULTI_ID), + [CIF_REG_DVP_SAV_EAV] = CIF_REG(DVP_SAV_EAV), + [CIF_REG_DVP_FRM0_ADDR_Y] = CIF_REG(DVP_FRM0_ADDR_Y_ID0), + [CIF_REG_DVP_FRM0_ADDR_UV] = CIF_REG(DVP_FRM0_ADDR_UV_ID0), + [CIF_REG_DVP_FRM1_ADDR_Y] = CIF_REG(DVP_FRM1_ADDR_Y_ID0), + [CIF_REG_DVP_FRM1_ADDR_UV] = CIF_REG(DVP_FRM1_ADDR_UV_ID0), + [CIF_REG_DVP_FRM0_ADDR_Y_ID1] = CIF_REG(DVP_FRM0_ADDR_Y_ID1), + [CIF_REG_DVP_FRM0_ADDR_UV_ID1] = CIF_REG(DVP_FRM0_ADDR_UV_ID1), + [CIF_REG_DVP_FRM1_ADDR_Y_ID1] = CIF_REG(DVP_FRM1_ADDR_Y_ID1), + [CIF_REG_DVP_FRM1_ADDR_UV_ID1] = CIF_REG(DVP_FRM1_ADDR_UV_ID1), + [CIF_REG_DVP_FRM0_ADDR_Y_ID2] = CIF_REG(DVP_FRM0_ADDR_Y_ID2), + [CIF_REG_DVP_FRM0_ADDR_UV_ID2] = CIF_REG(DVP_FRM0_ADDR_UV_ID2), + [CIF_REG_DVP_FRM1_ADDR_Y_ID2] = CIF_REG(DVP_FRM1_ADDR_Y_ID2), + [CIF_REG_DVP_FRM1_ADDR_UV_ID2] = CIF_REG(DVP_FRM1_ADDR_UV_ID2), + [CIF_REG_DVP_FRM0_ADDR_Y_ID3] = CIF_REG(DVP_FRM0_ADDR_Y_ID3), + [CIF_REG_DVP_FRM0_ADDR_UV_ID3] = CIF_REG(DVP_FRM0_ADDR_UV_ID3), + [CIF_REG_DVP_FRM1_ADDR_Y_ID3] = CIF_REG(DVP_FRM1_ADDR_Y_ID3), + [CIF_REG_DVP_FRM1_ADDR_UV_ID3] = CIF_REG(DVP_FRM1_ADDR_UV_ID3), + [CIF_REG_DVP_VIR_LINE_WIDTH] = CIF_REG(DVP_VIR_LINE_WIDTH), + [CIF_REG_DVP_SET_SIZE] = CIF_REG(DVP_CROP_SIZE), + [CIF_REG_DVP_LINE_INT_NUM] = CIF_REG(DVP_LINE_INT_NUM_01), + [CIF_REG_DVP_LINE_INT_NUM1] = CIF_REG(DVP_LINE_INT_NUM_23), + [CIF_REG_DVP_LINE_CNT] = CIF_REG(DVP_LINE_INT_NUM_01), + [CIF_REG_DVP_LINE_CNT1] = CIF_REG(DVP_LINE_INT_NUM_23), + + [CIF_REG_MIPI_LVDS_ID0_CTRL0] = CIF_REG(CSI_MIPI0_ID0_CTRL0), + [CIF_REG_MIPI_LVDS_ID0_CTRL1] = CIF_REG(CSI_MIPI0_ID0_CTRL1), + [CIF_REG_MIPI_LVDS_ID1_CTRL0] = CIF_REG(CSI_MIPI0_ID1_CTRL0), + [CIF_REG_MIPI_LVDS_ID1_CTRL1] = CIF_REG(CSI_MIPI0_ID1_CTRL1), + [CIF_REG_MIPI_LVDS_ID2_CTRL0] = CIF_REG(CSI_MIPI0_ID2_CTRL0), + [CIF_REG_MIPI_LVDS_ID2_CTRL1] = CIF_REG(CSI_MIPI0_ID2_CTRL1), + [CIF_REG_MIPI_LVDS_ID3_CTRL0] = CIF_REG(CSI_MIPI0_ID3_CTRL0), + [CIF_REG_MIPI_LVDS_ID3_CTRL1] = CIF_REG(CSI_MIPI0_ID3_CTRL1), + [CIF_REG_MIPI_LVDS_CTRL] = CIF_REG(CSI_MIPI0_CTRL), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0] = CIF_REG(CSI_MIPI0_FRM0_ADDR_Y_ID0), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0] = CIF_REG(CSI_MIPI0_FRM1_ADDR_Y_ID0), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0] = CIF_REG(CSI_MIPI0_FRM0_ADDR_UV_ID0), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0] = CIF_REG(CSI_MIPI0_FRM1_ADDR_UV_ID0), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0] = CIF_REG(CSI_MIPI0_VLW_ID0), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1] = CIF_REG(CSI_MIPI0_FRM0_ADDR_Y_ID1), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1] = CIF_REG(CSI_MIPI0_FRM1_ADDR_Y_ID1), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1] = CIF_REG(CSI_MIPI0_FRM0_ADDR_UV_ID1), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1] = CIF_REG(CSI_MIPI0_FRM1_ADDR_UV_ID1), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1] = CIF_REG(CSI_MIPI0_VLW_ID1), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2] = CIF_REG(CSI_MIPI0_FRM0_ADDR_Y_ID2), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2] = CIF_REG(CSI_MIPI0_FRM1_ADDR_Y_ID2), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2] = CIF_REG(CSI_MIPI0_FRM0_ADDR_UV_ID2), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2] = CIF_REG(CSI_MIPI0_FRM1_ADDR_UV_ID2), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2] = CIF_REG(CSI_MIPI0_VLW_ID2), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3] = CIF_REG(CSI_MIPI0_FRM0_ADDR_Y_ID3), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3] = CIF_REG(CSI_MIPI0_FRM1_ADDR_Y_ID3), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3] = CIF_REG(CSI_MIPI0_FRM0_ADDR_UV_ID3), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3] = CIF_REG(CSI_MIPI0_FRM1_ADDR_UV_ID3), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3] = CIF_REG(CSI_MIPI0_VLW_ID3), + [CIF_REG_MIPI_LVDS_INTEN] = CIF_REG(CSI_MIPI0_INTEN), + [CIF_REG_MIPI_LVDS_INTSTAT] = CIF_REG(CSI_MIPI0_INTSTAT), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1] = CIF_REG(CSI_MIPI0_LINE_INT_NUM_ID0_1), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3] = CIF_REG(CSI_MIPI0_LINE_INT_NUM_ID2_3), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1] = CIF_REG(CSI_MIPI0_LINE_CNT_ID0_1), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID2_3] = CIF_REG(CSI_MIPI0_LINE_CNT_ID2_3), + [CIF_REG_MIPI_LVDS_ID0_CROP_START] = CIF_REG(CSI_MIPI0_ID0_CROP_START), + [CIF_REG_MIPI_LVDS_ID1_CROP_START] = CIF_REG(CSI_MIPI0_ID1_CROP_START), + [CIF_REG_MIPI_LVDS_ID2_CROP_START] = CIF_REG(CSI_MIPI0_ID2_CROP_START), + [CIF_REG_MIPI_LVDS_ID3_CROP_START] = CIF_REG(CSI_MIPI0_ID3_CROP_START), + [CIF_REG_MIPI_FRAME_NUM_VC0] = CIF_REG(CSI_MIPI0_FRAME_NUM_VC0), + [CIF_REG_MIPI_FRAME_NUM_VC1] = CIF_REG(CSI_MIPI0_FRAME_NUM_VC1), + [CIF_REG_MIPI_FRAME_NUM_VC2] = CIF_REG(CSI_MIPI0_FRAME_NUM_VC2), + [CIF_REG_MIPI_FRAME_NUM_VC3] = CIF_REG(CSI_MIPI0_FRAME_NUM_VC3), + [CIF_REG_MIPI_EFFECT_CODE_ID0] = CIF_REG(CSI_MIPI0_EFFECT_CODE_ID0), + [CIF_REG_MIPI_EFFECT_CODE_ID1] = CIF_REG(CSI_MIPI0_EFFECT_CODE_ID1), + [CIF_REG_MIPI_EFFECT_CODE_ID2] = CIF_REG(CSI_MIPI0_EFFECT_CODE_ID2), + [CIF_REG_MIPI_EFFECT_CODE_ID3] = CIF_REG(CSI_MIPI0_EFFECT_CODE_ID3), + [CIF_REG_MIPI_ON_PAD] = CIF_REG(CSI_MIPI0_ON_PAD), + + [CIF_REG_GLB_CTRL] = CIF_REG(GLB_CTRL), + [CIF_REG_GLB_INTEN] = CIF_REG(GLB_INTEN), + [CIF_REG_GLB_INTST] = CIF_REG(GLB_INTST), + + [CIF_REG_SCL_CH_CTRL] = CIF_REG(SCL_CH_CTRL), + [CIF_REG_SCL_CTRL] = CIF_REG(SCL_CTRL), + [CIF_REG_SCL_FRM0_ADDR_CH0] = CIF_REG(SCL_FRM0_ADDR_CH0), + [CIF_REG_SCL_FRM1_ADDR_CH0] = CIF_REG(SCL_FRM1_ADDR_CH0), + [CIF_REG_SCL_VLW_CH0] = CIF_REG(SCL_VLW_CH0), + [CIF_REG_SCL_FRM0_ADDR_CH1] = CIF_REG(SCL_FRM0_ADDR_CH1), + [CIF_REG_SCL_FRM1_ADDR_CH1] = CIF_REG(SCL_FRM1_ADDR_CH1), + [CIF_REG_SCL_VLW_CH1] = CIF_REG(SCL_VLW_CH1), + [CIF_REG_SCL_FRM0_ADDR_CH2] = CIF_REG(SCL_FRM0_ADDR_CH2), + [CIF_REG_SCL_FRM1_ADDR_CH2] = CIF_REG(SCL_FRM1_ADDR_CH2), + [CIF_REG_SCL_VLW_CH2] = CIF_REG(SCL_VLW_CH2), + [CIF_REG_SCL_FRM0_ADDR_CH3] = CIF_REG(SCL_FRM0_ADDR_CH3), + [CIF_REG_SCL_FRM1_ADDR_CH3] = CIF_REG(SCL_FRM1_ADDR_CH3), + [CIF_REG_SCL_VLW_CH3] = CIF_REG(SCL_VLW_CH3), + [CIF_REG_SCL_BLC_CH0] = CIF_REG(SCL_BLC_CH0), + [CIF_REG_SCL_BLC_CH1] = CIF_REG(SCL_BLC_CH1), + [CIF_REG_SCL_BLC_CH2] = CIF_REG(SCL_BLC_CH2), + [CIF_REG_SCL_BLC_CH3] = CIF_REG(SCL_BLC_CH3), + [CIF_REG_TOISP0_CTRL] = CIF_REG(TOISP0_CH_CTRL), + [CIF_REG_TOISP0_SIZE] = CIF_REG(TOISP0_CROP_SIZE), + [CIF_REG_TOISP0_CROP] = CIF_REG(TOISP0_CROP), + [CIF_REG_TOISP1_CTRL] = CIF_REG(TOISP1_CH_CTRL), + [CIF_REG_TOISP1_SIZE] = CIF_REG(TOISP1_CROP_SIZE), + [CIF_REG_TOISP1_CROP] = CIF_REG(TOISP1_CROP), + [CIF_REG_GRF_CIFIO_CON] = CIF_REG(CIF_GRF_SOC_CON2), +}; + static const struct rkcif_hw_match_data px30_cif_match_data = { .chip_id = CHIP_PX30_CIF, .clks = px30_cif_clks, @@ -678,6 +802,14 @@ static const struct rkcif_hw_match_data rk3568_cif_match_data = { .cif_regs = rk3568_cif_regs, }; +static const struct rkcif_hw_match_data rk3588_cif_match_data = { + .chip_id = CHIP_RK3588_CIF, + .clks = rk3588_cif_clks, + .clks_num = ARRAY_SIZE(rk3588_cif_clks), + .rsts = rk3588_cif_rsts, + .rsts_num = ARRAY_SIZE(rk3588_cif_rsts), + .cif_regs = rk3588_cif_regs, +}; static const struct of_device_id rkcif_plat_of_match[] = { { @@ -708,6 +840,10 @@ static const struct of_device_id rkcif_plat_of_match[] = { .compatible = "rockchip,rk3568-cif", .data = &rk3568_cif_match_data, }, + { + .compatible = "rockchip,rk3588-cif", + .data = &rk3588_cif_match_data, + }, { .compatible = "rockchip,rv1126-cif", .data = &rv1126_cif_match_data, @@ -723,11 +859,17 @@ static irqreturn_t rkcif_irq_handler(int irq, void *ctx) { struct device *dev = ctx; struct rkcif_hw *cif_hw = dev_get_drvdata(dev); + unsigned int intstat_glb = 0; int i; + if (cif_hw->chip_id == CHIP_RK3588_CIF) + intstat_glb = rkcif_irq_global(cif_hw->cif_dev[0]); for (i = 0; i < cif_hw->dev_num; i++) { - if (cif_hw->cif_dev[i]->isr_hdl) + if (cif_hw->cif_dev[i]->isr_hdl) { cif_hw->cif_dev[i]->isr_hdl(irq, cif_hw->cif_dev[i]); + if (cif_hw->chip_id == CHIP_RK3588_CIF && intstat_glb) + rkcif_irq_handle_toisp(cif_hw->cif_dev[i], intstat_glb); + } } return IRQ_HANDLED; @@ -825,6 +967,7 @@ static int rkcif_plat_hw_probe(struct platform_device *pdev) const struct rkcif_hw_match_data *data; struct resource *res; int i, ret, irq; + bool is_mem_reserved = false; match = of_match_node(rkcif_plat_of_match, node); if (IS_ERR(match)) @@ -853,10 +996,7 @@ static int rkcif_plat_hw_probe(struct platform_device *pdev) cif_hw->irq = irq; cif_hw->match_data = data; cif_hw->chip_id = data->chip_id; - if (data->chip_id == CHIP_RK1808_CIF || - data->chip_id == CHIP_RV1126_CIF || - data->chip_id == CHIP_RV1126_CIF_LITE || - data->chip_id == CHIP_RK3568_CIF) { + if (data->chip_id >= CHIP_RK1808_CIF) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cif_regs"); @@ -915,17 +1055,30 @@ static int rkcif_plat_hw_probe(struct platform_device *pdev) cif_hw->cif_regs = data->cif_regs; + cif_hw->is_dma_contig = true; + cif_hw->is_dma_sg_ops = false; + mutex_init(&cif_hw->dev_lock); + cif_hw->iommu_en = is_iommu_enable(dev); - if (!cif_hw->iommu_en) { - ret = of_reserved_mem_device_init(dev); - if (ret) + ret = of_reserved_mem_device_init(dev); + if (ret) { + is_mem_reserved = false; + if (!cif_hw->iommu_en) dev_info(dev, "No reserved memory region assign to CIF\n"); + else + cif_hw->is_dma_contig = false; + } + if (is_mem_reserved) { + cif_hw->mem_ops = &vb2_rdma_sg_memops; + cif_hw->is_dma_sg_ops = true; + } else if (cif_hw->iommu_en) { + cif_hw->mem_ops = &vb2_dma_sg_memops; + cif_hw->is_dma_sg_ops = true; + } else { + cif_hw->mem_ops = &vb2_dma_contig_memops; } - if (data->chip_id != CHIP_RK1808_CIF && - data->chip_id != CHIP_RV1126_CIF && - data->chip_id != CHIP_RV1126_CIF_LITE && - data->chip_id != CHIP_RK3568_CIF) { + if (data->chip_id < CHIP_RK1808_CIF) { cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL); if (!cif_dev) return -ENOMEM; @@ -946,9 +1099,8 @@ static int rkcif_plat_hw_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - if (data->chip_id == CHIP_RK1808_CIF || - data->chip_id == CHIP_RV1126_CIF || - data->chip_id == CHIP_RK3568_CIF) { + if (data->chip_id >= CHIP_RK1808_CIF && + data->chip_id != CHIP_RV1126_CIF_LITE) { platform_driver_register(&rkcif_plat_drv); platform_driver_register(&rkcif_subdev_driver); } @@ -965,10 +1117,7 @@ static int rkcif_plat_remove(struct platform_device *pdev) rkcif_iommu_cleanup(cif_hw); mutex_destroy(&cif_hw->dev_lock); - if (cif_hw->chip_id != CHIP_RK1808_CIF && - cif_hw->chip_id != CHIP_RV1126_CIF && - cif_hw->chip_id != CHIP_RV1126_CIF_LITE && - cif_hw->chip_id != CHIP_RK3568_CIF) + if (cif_hw->chip_id < CHIP_RK1808_CIF) rkcif_plat_uninit(cif_hw->cif_dev[0]); return 0; diff --git a/drivers/media/platform/rockchip/cif/hw.h b/drivers/media/platform/rockchip/cif/hw.h index 573abe48c8b9..401af2bc33d5 100644 --- a/drivers/media/platform/rockchip/cif/hw.h +++ b/drivers/media/platform/rockchip/cif/hw.h @@ -19,7 +19,7 @@ #include "regs.h" #include "version.h" -#define RKCIF_DEV_MAX 2 +#define RKCIF_DEV_MAX 7 #define RKCIF_HW_DRIVER_NAME "rkcifhw" #define RKCIF_MAX_BUS_CLK 8 #define RKCIF_MAX_RESET 15 @@ -47,6 +47,7 @@ enum rkcif_chip_id { CHIP_RV1126_CIF, CHIP_RV1126_CIF_LITE, CHIP_RK3568_CIF, + CHIP_RK3588_CIF, }; struct rkcif_hw_match_data { @@ -72,17 +73,19 @@ struct rkcif_hw { struct regmap *grf; struct clk *clks[RKCIF_MAX_BUS_CLK]; int clk_size; - bool iommu_en; struct iommu_domain *domain; struct reset_control *cif_rst[RKCIF_MAX_RESET]; int chip_id; const struct cif_reg *cif_regs; + const struct vb2_mem_ops *mem_ops; + bool iommu_en; bool can_be_reset; + bool is_dma_sg_ops; + bool is_dma_contig; + struct rkcif_device *cif_dev[RKCIF_DEV_MAX]; + int dev_num; - struct rkcif_device *cif_dev[RKCIF_DEV_MAX]; - int dev_num; - - atomic_t power_cnt; + atomic_t power_cnt; const struct rkcif_hw_match_data *match_data; struct mutex dev_lock; }; diff --git a/drivers/media/platform/rockchip/cif/mipi-csi2.c b/drivers/media/platform/rockchip/cif/mipi-csi2.c index a69f738bcc2b..449f45da8356 100644 --- a/drivers/media/platform/rockchip/cif/mipi-csi2.c +++ b/drivers/media/platform/rockchip/cif/mipi-csi2.c @@ -31,9 +31,10 @@ MODULE_PARM_DESC(debug_csi2, "Debug level (0-1)"); * the 4 virtual channel output pads */ #define CSI2_SINK_PAD 0 -#define CSI2_NUM_SINK_PADS 1 -#define CSI2_NUM_SRC_PADS 4 +#define CSI2_NUM_SINK_PADS 4 +#define CSI2_NUM_SRC_PADS 8 #define CSI2_NUM_PADS 5 +#define CSI2_NUM_PADS_MAX 9 #define CSI2_NUM_PADS_SINGLE_LINK 2 #define MAX_CSI2_SENSORS 2 @@ -60,6 +61,7 @@ enum rkcsi2_chip_id { CHIP_RK3288_CSI2, CHIP_RV1126_CSI2, CHIP_RK3568_CSI2, + CHIP_RK3588_CSI2, }; enum csi2_pads { @@ -103,7 +105,7 @@ struct csi2_err_stats { struct csi2_dev { struct device *dev; struct v4l2_subdev sd; - struct media_pad pad[CSI2_NUM_PADS]; + struct media_pad pad[CSI2_NUM_PADS_MAX]; struct clk_bulk_data *clks_bulk; int clks_num; struct reset_control *rsts_bulk; @@ -277,25 +279,36 @@ static void csi2_disable(struct csi2_dev *csi2) write_csihost_reg(base, CSIHOST_MSK2, 0xffffffff); } +static int csi2_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *mbus); + static void csi2_enable(struct csi2_dev *csi2, enum host_type_t host_type) { void __iomem *base = csi2->base; int lanes = csi2->bus.num_data_lanes; + struct v4l2_mbus_config mbus; + u32 val = 0; + + csi2_g_mbus_config(&csi2->sd, 0, &mbus); + if (mbus.type == V4L2_MBUS_CSI2_DPHY) + val = SW_CPHY_EN(0); + else if (mbus.type == V4L2_MBUS_CSI2_CPHY) + val = SW_CPHY_EN(1); write_csihost_reg(base, CSIHOST_N_LANES, lanes - 1); if (host_type == RK_DSI_RXHOST) { - write_csihost_reg(base, CSIHOST_CONTROL, - SW_CPHY_EN(0) | SW_DSI_EN(1) | - SW_DATATYPE_FS(0x01) | SW_DATATYPE_FE(0x11) | - SW_DATATYPE_LS(0x21) | SW_DATATYPE_LE(0x31)); + val |= SW_DSI_EN(1) | SW_DATATYPE_FS(0x01) | + SW_DATATYPE_FE(0x11) | SW_DATATYPE_LS(0x21) | + SW_DATATYPE_LE(0x31); + write_csihost_reg(base, CSIHOST_CONTROL, val); /* Disable some error interrupt when HOST work on DSI RX mode */ write_csihost_reg(base, CSIHOST_MSK1, 0xe00000f0); write_csihost_reg(base, CSIHOST_MSK2, 0xff00); } else { - write_csihost_reg(base, CSIHOST_CONTROL, - SW_CPHY_EN(0) | SW_DSI_EN(0)); + val |= SW_DSI_EN(0); + write_csihost_reg(base, CSIHOST_CONTROL, val); write_csihost_reg(base, CSIHOST_MSK1, 0); write_csihost_reg(base, CSIHOST_MSK2, 0xf000); } @@ -889,6 +902,11 @@ static const struct csi2_match_data rk3568_csi2_match_data = { .num_pads = CSI2_NUM_PADS, }; +static const struct csi2_match_data rk3588_csi2_match_data = { + .chip_id = CHIP_RK3588_CSI2, + .num_pads = CSI2_NUM_PADS_MAX, +}; + static const struct of_device_id csi2_dt_ids[] = { { .compatible = "rockchip,rk1808-mipi-csi2", @@ -906,6 +924,10 @@ static const struct of_device_id csi2_dt_ids[] = { .compatible = "rockchip,rv1126-mipi-csi2", .data = &rv1126_csi2_match_data, }, + { + .compatible = "rockchip,rk3588-mipi-csi2", + .data = &rk3588_csi2_match_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, csi2_dt_ids); diff --git a/drivers/media/platform/rockchip/cif/procfs.c b/drivers/media/platform/rockchip/cif/procfs.c index 51c75b7415e4..43d6e2c232b0 100644 --- a/drivers/media/platform/rockchip/cif/procfs.c +++ b/drivers/media/platform/rockchip/cif/procfs.c @@ -272,7 +272,8 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f) mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ? "high active" : "low active"); } else { seq_printf(f, "\tinterface:%s\n", - sensor->mbus.type == V4L2_MBUS_CSI2_DPHY ? "mipi csi2" : + sensor->mbus.type == V4L2_MBUS_CSI2_DPHY ? "mipi csi2 dphy" : + sensor->mbus.type == V4L2_MBUS_CSI2_CPHY ? "mipi csi2 cphy" : sensor->mbus.type == V4L2_MBUS_CCP2 ? "lvds" : "unknown"); seq_printf(f, "\tlanes:%d\n", sensor->lanes); seq_puts(f, "\tvc channel:"); @@ -288,8 +289,8 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f) } seq_printf(f, "\thdr mode: %s\n", - dev->hdr.mode == NO_HDR ? "normal" : - dev->hdr.mode == HDR_X2 ? "hdr_x2" : "hdr_x3"); + dev->hdr.hdr_mode == NO_HDR ? "normal" : + dev->hdr.hdr_mode == HDR_X2 ? "hdr_x2" : "hdr_x3"); seq_printf(f, "\tformat:%s/%ux%u@%d\n", rkcif_pixelcode_to_string(stream->cif_fmt_in->mbus_code), @@ -318,7 +319,7 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f) if (dev->inf_id == RKCIF_MIPI_LVDS) { time_val = div_u64(stream->readout.early_time, 1000000); seq_printf(f, "\tearly:%u ms\n", time_val); - if (dev->hdr.mode == NO_HDR) { + if (dev->hdr.hdr_mode == NO_HDR) { time_val = div_u64(stream->readout.readout_time, 1000000); seq_printf(f, "\treadout:%u ms\n", time_val); } else { @@ -341,10 +342,12 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f) seq_printf(f, "\t\t\tdvp pix err:%llu\n", dev->irq_stats.dvp_pix_err_cnt); seq_printf(f, "\t\t\tdvp line err:%llu\n", dev->irq_stats.dvp_line_err_cnt); seq_printf(f, "\t\t\tdvp over flow:%llu\n", dev->irq_stats.dvp_overflow_cnt); + seq_printf(f, "\t\t\tdvp bandwidth lack:%llu\n", dev->irq_stats.dvp_bwidth_lack_cnt); + seq_printf(f, "\t\t\tdvp size err:%llu\n", dev->irq_stats.dvp_size_err_cnt); } else { seq_printf(f, "\t\t\tcsi over flow:%llu\n", dev->irq_stats.csi_overflow_cnt); seq_printf(f, "\t\t\tcsi bandwidth lack:%llu\n", dev->irq_stats.csi_bwidth_lack_cnt); - + seq_printf(f, "\t\t\tcsi size err:%llu\n", dev->irq_stats.csi_size_err_cnt); } seq_printf(f, "\t\t\tall err count:%llu\n", dev->irq_stats.all_err_cnt); seq_printf(f, "\t\t\tframe dma end:%llu\n", dev->irq_stats.all_frm_end_cnt); diff --git a/drivers/media/platform/rockchip/cif/regs.h b/drivers/media/platform/rockchip/cif/regs.h index 44a8f392db18..e20365d841db 100644 --- a/drivers/media/platform/rockchip/cif/regs.h +++ b/drivers/media/platform/rockchip/cif/regs.h @@ -61,6 +61,9 @@ enum cif_reg_index { CIF_REG_DVP_FRM0_ADDR_UV_ID3, CIF_REG_DVP_FRM1_ADDR_Y_ID3, CIF_REG_DVP_FRM1_ADDR_UV_ID3, + CIF_REG_DVP_SAV_EAV, + CIF_REG_DVP_LINE_CNT1, + CIF_REG_DVP_LINE_INT_NUM1, /* mipi & lvds registers index */ CIF_REG_MIPI_LVDS_ID0_CTRL0, CIF_REG_MIPI_LVDS_ID0_CTRL1, @@ -134,6 +137,12 @@ enum cif_reg_index { CIF_REG_LVDS_SAV_EAV_BLK0_ID3, CIF_REG_LVDS_SAV_EAV_ACT1_ID3, CIF_REG_LVDS_SAV_EAV_BLK1_ID3, + CIF_REG_MIPI_EFFECT_CODE_ID0, + CIF_REG_MIPI_EFFECT_CODE_ID1, + CIF_REG_MIPI_EFFECT_CODE_ID2, + CIF_REG_MIPI_EFFECT_CODE_ID3, + CIF_REG_MIPI_ON_PAD, + CIF_REG_Y_STAT_CONTROL, CIF_REG_Y_STAT_VALUE, CIF_REG_MMU_DTE_ADDR, @@ -149,6 +158,34 @@ enum cif_reg_index { /* reg belowed is in grf */ CIF_REG_GRF_CIFIO_CON, CIF_REG_GRF_CIFIO_CON1, + /* reg global control */ + CIF_REG_GLB_CTRL, + CIF_REG_GLB_INTEN, + CIF_REG_GLB_INTST, + CIF_REG_SCL_CH_CTRL, + CIF_REG_SCL_CTRL, + CIF_REG_SCL_FRM0_ADDR_CH0, + CIF_REG_SCL_FRM1_ADDR_CH0, + CIF_REG_SCL_VLW_CH0, + CIF_REG_SCL_FRM0_ADDR_CH1, + CIF_REG_SCL_FRM1_ADDR_CH1, + CIF_REG_SCL_VLW_CH1, + CIF_REG_SCL_FRM0_ADDR_CH2, + CIF_REG_SCL_FRM1_ADDR_CH2, + CIF_REG_SCL_VLW_CH2, + CIF_REG_SCL_FRM0_ADDR_CH3, + CIF_REG_SCL_FRM1_ADDR_CH3, + CIF_REG_SCL_VLW_CH3, + CIF_REG_SCL_BLC_CH0, + CIF_REG_SCL_BLC_CH1, + CIF_REG_SCL_BLC_CH2, + CIF_REG_SCL_BLC_CH3, + CIF_REG_TOISP0_CTRL, + CIF_REG_TOISP0_SIZE, + CIF_REG_TOISP0_CROP, + CIF_REG_TOISP1_CTRL, + CIF_REG_TOISP1_SIZE, + CIF_REG_TOISP1_CROP, CIF_REG_INDEX_MAX }; @@ -298,6 +335,116 @@ enum cif_reg_index { #define CIF_MMU_INT_STATUS 0x820 #define CIF_MMU_AUTO_GATING 0x824 +/* RK3588 DVP Registers Offset */ +#define DVP_CTRL 0x10 +#define DVP_INTEN 0x14 +#define DVP_INTSTAT 0x18 +#define DVP_FOR 0x1C +#define DVP_MULTI_ID 0x20 +#define DVP_SAV_EAV 0x24 +#define DVP_CROP_SIZE 0x28 +#define DVP_CROP 0x2C +#define DVP_FRM0_ADDR_Y_ID0 0x30 +#define DVP_FRM0_ADDR_UV_ID0 0x34 +#define DVP_FRM1_ADDR_Y_ID0 0x38 +#define DVP_FRM1_ADDR_UV_ID0 0x3C +#define DVP_FRM0_ADDR_Y_ID1 0x40 +#define DVP_FRM0_ADDR_UV_ID1 0x44 +#define DVP_FRM1_ADDR_Y_ID1 0x48 +#define DVP_FRM1_ADDR_UV_ID1 0x4C +#define DVP_FRM0_ADDR_Y_ID2 0x50 +#define DVP_FRM0_ADDR_UV_ID2 0x54 +#define DVP_FRM1_ADDR_Y_ID2 0x58 +#define DVP_FRM1_ADDR_UV_ID2 0x5C +#define DVP_FRM0_ADDR_Y_ID3 0x60 +#define DVP_FRM0_ADDR_UV_ID3 0x64 +#define DVP_FRM1_ADDR_Y_ID3 0x68 +#define DVP_FRM1_ADDR_UV_ID3 0x6C +#define DVP_VIR_LINE_WIDTH 0x70 +#define DVP_LINE_INT_NUM_01 0x74 +#define DVP_LINE_INT_NUM_23 0x78 +#define DVP_LINE_CNT_01 0x7C +#define DVP_LINE_CNT_23 0x80 + +/* RK3588 CSI Registers Offset */ +#define CSI_MIPI0_ID0_CTRL0 0x100 +#define CSI_MIPI0_ID0_CTRL1 0x104 +#define CSI_MIPI0_ID1_CTRL0 0x108 +#define CSI_MIPI0_ID1_CTRL1 0x10C +#define CSI_MIPI0_ID2_CTRL0 0x110 +#define CSI_MIPI0_ID2_CTRL1 0x114 +#define CSI_MIPI0_ID3_CTRL0 0x118 +#define CSI_MIPI0_ID3_CTRL1 0x11C +#define CSI_MIPI0_CTRL 0x120 +#define CSI_MIPI0_FRM0_ADDR_Y_ID0 0x124 +#define CSI_MIPI0_FRM1_ADDR_Y_ID0 0x128 +#define CSI_MIPI0_FRM0_ADDR_UV_ID0 0x12C +#define CSI_MIPI0_FRM1_ADDR_UV_ID0 0x130 +#define CSI_MIPI0_VLW_ID0 0x134 +#define CSI_MIPI0_FRM0_ADDR_Y_ID1 0x138 +#define CSI_MIPI0_FRM1_ADDR_Y_ID1 0x13C +#define CSI_MIPI0_FRM0_ADDR_UV_ID1 0x140 +#define CSI_MIPI0_FRM1_ADDR_UV_ID1 0x144 +#define CSI_MIPI0_VLW_ID1 0x148 +#define CSI_MIPI0_FRM0_ADDR_Y_ID2 0x14C +#define CSI_MIPI0_FRM1_ADDR_Y_ID2 0x150 +#define CSI_MIPI0_FRM0_ADDR_UV_ID2 0x154 +#define CSI_MIPI0_FRM1_ADDR_UV_ID2 0x158 +#define CSI_MIPI0_VLW_ID2 0x15C +#define CSI_MIPI0_FRM0_ADDR_Y_ID3 0x160 +#define CSI_MIPI0_FRM1_ADDR_Y_ID3 0x164 +#define CSI_MIPI0_FRM0_ADDR_UV_ID3 0x168 +#define CSI_MIPI0_FRM1_ADDR_UV_ID3 0x16C +#define CSI_MIPI0_VLW_ID3 0x170 +#define CSI_MIPI0_INTEN 0x174 +#define CSI_MIPI0_INTSTAT 0x178 +#define CSI_MIPI0_LINE_INT_NUM_ID0_1 0x17C +#define CSI_MIPI0_LINE_INT_NUM_ID2_3 0x180 +#define CSI_MIPI0_LINE_CNT_ID0_1 0x184 +#define CSI_MIPI0_LINE_CNT_ID2_3 0x188 +#define CSI_MIPI0_ID0_CROP_START 0x18C +#define CSI_MIPI0_ID1_CROP_START 0x190 +#define CSI_MIPI0_ID2_CROP_START 0x194 +#define CSI_MIPI0_ID3_CROP_START 0x198 +#define CSI_MIPI0_FRAME_NUM_VC0 0x19C +#define CSI_MIPI0_FRAME_NUM_VC1 0x1A0 +#define CSI_MIPI0_FRAME_NUM_VC2 0x1A4 +#define CSI_MIPI0_FRAME_NUM_VC3 0x1A8 +#define CSI_MIPI0_EFFECT_CODE_ID0 0x1AC +#define CSI_MIPI0_EFFECT_CODE_ID1 0x1B0 +#define CSI_MIPI0_EFFECT_CODE_ID2 0x1B4 +#define CSI_MIPI0_EFFECT_CODE_ID3 0x1B8 +#define CSI_MIPI0_ON_PAD 0x1BC + +/* RK3588 CONTROL Registers Offset */ +#define GLB_CTRL 0X000 +#define GLB_INTEN 0X004 +#define GLB_INTST 0X008 +#define SCL_CH_CTRL 0x700 +#define SCL_CTRL 0x704 +#define SCL_FRM0_ADDR_CH0 0x708 +#define SCL_FRM1_ADDR_CH0 0x70C +#define SCL_VLW_CH0 0x710 +#define SCL_FRM0_ADDR_CH1 0x714 +#define SCL_FRM1_ADDR_CH1 0x718 +#define SCL_VLW_CH1 0x71C +#define SCL_FRM0_ADDR_CH2 0x720 +#define SCL_FRM1_ADDR_CH2 0x724 +#define SCL_VLW_CH2 0x728 +#define SCL_FRM0_ADDR_CH3 0x72C +#define SCL_FRM1_ADDR_CH3 0x730 +#define SCL_VLW_CH3 0x734 +#define SCL_BLC_CH0 0x738 +#define SCL_BLC_CH1 0x73C +#define SCL_BLC_CH2 0x740 +#define SCL_BLC_CH3 0x744 +#define TOISP0_CH_CTRL 0x780 +#define TOISP0_CROP_SIZE 0x784 +#define TOISP0_CROP 0x788 +#define TOISP1_CH_CTRL 0x78C +#define TOISP1_CROP_SIZE 0x790 +#define TOISP1_CROP 0x794 + /* The key register bit description */ /* CIF_CTRL Reg */ @@ -307,6 +454,13 @@ enum cif_reg_index { #define MODE_PINGPONG (0x1 << 1) #define MODE_LINELOOP (0x2 << 1) #define AXI_BURST_16 (0xF << 12) +#define DVP_PRESS_EN (0x1 << 12) +#define DVP_HURRY_EN (0x1 << 8) +#define DVP_DMA_EN (0x1 << 1) +#define DVP_SW_WATER_LINE_75 (0x0 << 5) +#define DVP_SW_WATER_LINE_50 (0x1 << 5) +#define DVP_SW_WATER_LINE_25 (0x2 << 5) +#define DVP_SW_WATER_LINE_00 (0x3 << 5) /* CIF_INTEN */ #define INTEN_DISABLE (0x0 << 0) @@ -337,6 +491,22 @@ enum cif_reg_index { #define INTSTAT_ERR (0xFC) #define DVP_ALL_OVERFLOW (IFIFO_OVERFLOW | DFIFO_OVERFLOW) +#define DVP_FIFO_OVERFLOW (0x01 << 16) +#define DVP_BANDWIDTH_LACK (0x01 << 17) + +#define DVP_SIZE_ERR_ID0 (0x1 << 22) +#define DVP_SIZE_ERR_ID1 (0x1 << 23) +#define DVP_SIZE_ERR_ID2 (0x1 << 24) +#define DVP_SIZE_ERR_ID3 (0x1 << 25) + +#define DVP_SIZE_ERR (DVP_SIZE_ERR_ID0 |\ + DVP_SIZE_ERR_ID1 |\ + DVP_SIZE_ERR_ID2 |\ + DVP_SIZE_ERR_ID3) + +#define DVP_SW_PRESS_VALUE(val) (((val) & 0x7) << 13) +#define DVP_SW_HURRY_VALUE(val) (((val) & 0x7) << 9) + #define DVP_DMA_END_INTEN(id) \ ({ \ unsigned int mask; \ @@ -351,7 +521,7 @@ enum cif_reg_index { mask; \ }) -#define DVP_LINE_INTEN (0x01 << 10) +#define DVP_LINE_INTEN (0x01 << 10) #define DVP_DMA_END_INTSTAT(id) \ ({ \ @@ -367,8 +537,8 @@ enum cif_reg_index { mask; \ }) -#define DVP_PST_INTSTAT PST_INF_FRAME_END -#define DVP_LINE_INTSTAT (0x01 << 10) +#define DVP_PST_INTSTAT PST_INF_FRAME_END +#define DVP_LINE_INTSTAT (0x01 << 10) /* FRAME STATUS */ #define FRAME_STAT_CLS 0x00 @@ -433,6 +603,19 @@ enum cif_reg_index { #define BT656_1120_MULTI_ID_2_MASK ~(0x03 << 20) #define BT656_1120_MULTI_ID_3_MASK ~(0x03 << 28) #define CIF_HIGH_ALIGN (0x01 << 18) +#define CIF_HIGH_ALIGN_RK3588 (0x01 << 21) + + +#define BT1120_CLOCK_SINGLE_EDGES_RK3588 (0x00 << 11) +#define BT1120_CLOCK_DOUBLE_EDGES_RK3588 (0x01 << 11) +#define TRANSMIT_INTERFACE_RK3588 (0x01 << 9) +#define TRANSMIT_PROGRESS_RK3588 (0x00 << 9) +#define BT1120_YC_SWAP_RK3588 (0x01 << 12) +#define INPUT_BT601_YUV422 (0x00 << 2) +#define INPUT_BT601_RAW (0x01 << 2) +#define INPUT_BT656_YUV422 (0x02 << 2) +#define INPUT_BT1120_YUV422 (0x03 << 2) +#define INPUT_SONY_RAW (0x04 << 2) /* CIF_SCL_CTRL */ #define ENABLE_SCL_DOWN (0x01 << 0) @@ -460,11 +643,33 @@ enum cif_reg_index { #define DVP_CHANNEL3_F1_READY (0x01 << 13) #define DVP_CHANNEL3_FRM_READ (DVP_CHANNEL3_F0_READY | DVP_CHANNEL3_F1_READY) +#define DVP_FRAME0_START_ID0 (0x1 << 0) +#define DVP_FRAME1_START_ID0 (0x1 << 1) + #define DVP_FRAME_END_ID0 (0x1 << 0) #define DVP_FRAME_END_ID1 (0x1 << 11) #define DVP_FRAME_END_ID2 (0x1 << 12) #define DVP_FRAME_END_ID3 (0x1 << 13) +#define DVP_FRAME0_END_ID0 (0x1 << 8) +#define DVP_FRAME1_END_ID0 (0x1 << 9) +#define DVP_ALL_END_ID0 (DVP_FRAME0_END_ID0 | DVP_FRAME1_END_ID0) + +#define DVP_FRAME0_END_ID1 (0x1 << 10) +#define DVP_FRAME1_END_ID1 (0x1 << 11) +#define DVP_ALL_END_ID1 (DVP_FRAME0_END_ID1 | DVP_FRAME1_END_ID1) + +#define DVP_FRAME0_END_ID2 (0x1 << 12) +#define DVP_FRAME1_END_ID2 (0x1 << 13) +#define DVP_ALL_END_ID2 (DVP_FRAME0_END_ID2 | DVP_FRAME1_END_ID2) + +#define DVP_FRAME0_END_ID3 (0x1 << 14) +#define DVP_FRAME1_END_ID3 (0x1 << 15) +#define DVP_ALL_END_ID3 (DVP_FRAME0_END_ID3 | DVP_FRAME1_END_ID3) + +#define DVP_ALIGN_MSB (0x01 << 21) +#define DVP_ALIGN_LSB (0x00 << 21) + #define DVP_FRM_STS_ID0(x) (((x) & (0x3 << 0)) >> 0) #define DVP_FRM_STS_ID1(x) (((x) & (0x3 << 4)) >> 4) #define DVP_FRM_STS_ID2(x) (((x) & (0x3 << 8)) >> 8) @@ -497,6 +702,21 @@ enum cif_reg_index { #define CIF_CROP_Y_SHIFT 16 #define CIF_CROP_X_SHIFT 0 +/* CIF SCALE*/ +#define SCALE_END_INTSTAT(ch) (0x3 << ((ch + 1) * 2)) +#define SCALE_FIFO_OVERFLOW(ch) (1 << (10 + ch)) +#define SCALE_TOISP_AXI0_ERR (1 << 0) +#define SCALE_TOISP_AXI1_ERR (1 << 1) +#define CIF_SCALE_SW_PRESS_VALUE(val) (((val) & 0x7) << 13) +#define CIF_SCALE_SW_PRESS_ENABLE (0x1 << 12) +#define CIF_SCALE_SW_HURRY_VALUE(val) (((val) & 0x7) << 5) +#define CIF_SCALE_SW_HURRY_ENABLE (0x1 << 4) +#define CIF_SCALE_SW_WATER_LINE(val) (val << 1) +#define CIF_SCALE_SW_SRC_CH(val, ch) ((val & 0x1f) << (3 + ch * 8)) +#define CIF_SCALE_SW_MODE(val, ch) ((val & 0x3) << (1 + ch * 8)) +#define CIF_SCALE_EN(ch) (1 << (ch * 8)) +#define SW_SCALE_END(intstat, ch) ((intstat >> ((ch + 1) * 2)) & 0x3) + /* CIF_CSI_ID_CTRL0 */ #define CSI_DISABLE_CAPTURE (0x0 << 0) #define CSI_ENABLE_CAPTURE (0x1 << 0) @@ -511,12 +731,38 @@ enum cif_reg_index { #define CSI_ENABLE_COMMAND_MODE (0x1 << 4) #define CSI_DISABLE_CROP (0x0 << 5) #define CSI_ENABLE_CROP (0x1 << 5) +#define CSI_DISABLE_CROP_V1 (0x0 << 4) +#define CSI_ENABLE_CROP_V1 (0x1 << 4) #define CSI_ENABLE_MIPI_COMPACT (0x1 << 6) #define CSI_YUV_INPUT_ORDER_UYVY (0x0 << 16) #define CSI_YUV_INPUT_ORDER_VYUY (0x1 << 16) #define CSI_YUV_INPUT_ORDER_YUYV (0x2 << 16) #define CSI_YUV_INPUT_ORDER_YVYU (0x3 << 16) -#define CSI_ENABLE_MIPI_HIGH_ALIGN (0x1 << 31) +#define CSI_HIGH_ALIGN (0x1 << 31) +#define CSI_HIGH_ALIGN_RK3588 (0x1 << 27) + +#define CSI_YUV_OUTPUT_ORDER_UYVY (0x0 << 18) +#define CSI_YUV_OUTPUT_ORDER_VYUY (0x1 << 18) +#define CSI_YUV_OUTPUT_ORDER_YUYV (0x2 << 18) +#define CSI_YUV_OUTPUT_ORDER_YVYU (0x3 << 18) +#define CSI_WRDDR_TYPE_RAW_COMPACT (0x0 << 5) +#define CSI_WRDDR_TYPE_RAW_UNCOMPACT (0x1 << 5) +#define CSI_WRDDR_TYPE_YUV_PACKET (0x2 << 5) +#define CSI_WRDDR_TYPE_YUV400_RK3588 (0x3 << 5) +#define CSI_WRDDR_TYPE_YUV422SP_RK3588 (0x4 << 5) +#define CSI_WRDDR_TYPE_YUV420SP_RK3588 (0x5 << 5) +#define CSI_ALIGN_MSB (0x01 << 27) +#define CSI_ALIGN_LSB (0x0 << 27) +#define CSI_DMA_ENABLE (0x1 << 28) + +#define CSI_NO_HDR (0X0 << 22) +#define CSI_HDR2 (0X1 << 22) +#define CSI_HDR3 (0X2 << 22) + +#define CSI_HDR_MODE_VC (0x0 << 20) +#define CSI_HDR_MODE_LINE_CNT (0x1 << 20) +#define CSI_HDR_MODE_LINE_INFO (0x2 << 20) +#define CSI_HDR_VC_MODE_PROTECT (0x1 << 29) #define LVDS_ENABLE_CAPTURE (0x1 << 16) #define LVDS_MODE(mode) (((mode) & 0x7) << 17) @@ -561,14 +807,17 @@ enum cif_reg_index { #define CSI_ALL_FRAME_START_INTEN (0xff << 0) #define CSI_ALL_FRAME_END_INTEN (0xff << 8) #define CSI_ALL_ERROR_INTEN (0x1f << 16) +#define CSI_ALL_ERROR_INTEN_V1 (0xf0f << 16) #define CSI_START_INTEN(id) (0x3 << ((id) * 2)) #define CSI_DMA_END_INTEN(id) (0x3 << ((id) * 2 + 8)) #define CSI_LINE_INTEN(id) (0x1 << ((id) + 21)) +#define CSI_LINE_INTEN_RK3588(id) (0x1 << ((id) + 20)) #define CSI_START_INTSTAT(id) (0x3 << ((id) * 2)) #define CSI_DMA_END_INTSTAT(id) (0x3 << ((id) * 2 + 8)) #define CSI_LINE_INTSTAT(id) (0x1 << ((id) + 21)) +#define CSI_LINE_INTSTAT_V1(id) (0x1 << ((id) + 20)) /* CIF_CSI_INTSTAT */ #define CSI_FRAME0_START_ID0 (0x1 << 0) @@ -598,51 +847,72 @@ enum cif_reg_index { #define CSI_LINE_ID3_INTST (0x1 << 24) #define CSI_DMA_LVDS_ID2_FIFO_OVERFLOW (0x1 << 25) #define CSI_DMA_LVDS_ID3_FIFO_OVERFLOW (0x1 << 26) +#define CSI_SIZE_ERR_ID0 (0x1 << 24) +#define CSI_SIZE_ERR_ID1 (0x1 << 25) +#define CSI_SIZE_ERR_ID2 (0x1 << 26) +#define CSI_SIZE_ERR_ID3 (0x1 << 27) + +#define CSI_FRAME_START_ID0 (CSI_FRAME0_START_ID0 |\ + CSI_FRAME1_START_ID0) +#define CSI_FRAME_START_ID1 (CSI_FRAME0_START_ID1 |\ + CSI_FRAME1_START_ID1) +#define CSI_FRAME_START_ID2 (CSI_FRAME0_START_ID2 |\ + CSI_FRAME1_START_ID2) +#define CSI_FRAME_START_ID3 (CSI_FRAME0_START_ID3 |\ + CSI_FRAME1_START_ID3) +#define CSI_FRAME_END_ID0 (CSI_FRAME0_END_ID0 |\ + CSI_FRAME1_END_ID0) +#define CSI_FRAME_END_ID1 (CSI_FRAME0_END_ID1 |\ + CSI_FRAME1_END_ID1) +#define CSI_FRAME_END_ID2 (CSI_FRAME0_END_ID2 |\ + CSI_FRAME1_END_ID2) +#define CSI_FRAME_END_ID3 (CSI_FRAME0_END_ID3 |\ + CSI_FRAME1_END_ID3) +#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW |\ + CSI_DMA_UV_FIFO_OVERFLOW |\ + CSI_CONFIG_FIFO_OVERFLOW |\ + CSI_RX_FIFO_OVERFLOW |\ + CSI_DMA_LVDS_ID2_FIFO_OVERFLOW |\ + CSI_DMA_LVDS_ID3_FIFO_OVERFLOW) + +/*mask for rk3588*/ +#define CSI_RX_FIFO_OVERFLOW_V1 (0x1 << 19) +#define CSI_BANDWIDTH_LACK_V1 (0x1 << 18) +#define CSI_ALL_ERROR_INTEN_V1 (0xf0f << 16) + + +#define CSI_FIFO_OVERFLOW_V1 (CSI_DMA_Y_FIFO_OVERFLOW |\ + CSI_DMA_UV_FIFO_OVERFLOW |\ + CSI_RX_FIFO_OVERFLOW_V1) +#define CSI_SIZE_ERR (CSI_SIZE_ERR_ID0 |\ + CSI_SIZE_ERR_ID1 |\ + CSI_SIZE_ERR_ID2 |\ + CSI_SIZE_ERR_ID3) -#define CSI_FRAME_START_ID0 (CSI_FRAME0_START_ID0 |\ - CSI_FRAME1_START_ID0) -#define CSI_FRAME_START_ID1 (CSI_FRAME0_START_ID1 |\ - CSI_FRAME1_START_ID1) -#define CSI_FRAME_START_ID2 (CSI_FRAME0_START_ID2 |\ - CSI_FRAME1_START_ID2) -#define CSI_FRAME_START_ID3 (CSI_FRAME0_START_ID3 |\ - CSI_FRAME1_START_ID3) -#define CSI_FRAME_END_ID0 (CSI_FRAME0_END_ID0 |\ - CSI_FRAME1_END_ID0) -#define CSI_FRAME_END_ID1 (CSI_FRAME0_END_ID1 |\ - CSI_FRAME1_END_ID1) -#define CSI_FRAME_END_ID2 (CSI_FRAME0_END_ID2 |\ - CSI_FRAME1_END_ID2) -#define CSI_FRAME_END_ID3 (CSI_FRAME0_END_ID3 |\ - CSI_FRAME1_END_ID3) -#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW | \ - CSI_DMA_UV_FIFO_OVERFLOW | \ - CSI_CONFIG_FIFO_OVERFLOW | \ - CSI_RX_FIFO_OVERFLOW | \ - CSI_DMA_LVDS_ID2_FIFO_OVERFLOW | \ - CSI_DMA_LVDS_ID3_FIFO_OVERFLOW) /* CIF_MIPI_LVDS_CTRL */ -#define CIF_MIPI_LVDS_SW_DMA_IDLE (0x1 << 16) -#define CIF_MIPI_LVDS_SW_PRESS_VALUE(val) (((val) & 0x3) << 13) -#define CIF_MIPI_LVDS_SW_PRESS_ENABLE (0x1 << 12) -#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_8BITS (0x0 << 9) -#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_10BITS (0x1 << 9) -#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_12BITS (0x2 << 9) -#define CIF_MIPI_LVDS_SW_SEL_LVDS (0x1 << 8) -#define CIF_MIPI_LVDS_SW_HURRY_VALUE(val) (((val) & 0x3) << 5) -#define CIF_MIPI_LVDS_SW_HURRY_ENABLE (0x1 << 4) -#define CIF_MIPI_LVDS_SW_WATER_LINE_75 (0x0 << 1) -#define CIF_MIPI_LVDS_SW_WATER_LINE_50 (0x1 << 1) -#define CIF_MIPI_LVDS_SW_WATER_LINE_25 (0x2 << 1) -#define CIF_MIPI_LVDS_SW_WATER_LINE_00 (0x3 << 1) -#define CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE (0x1 << 0) -#define CIF_MIPI_LVDS_SW_DMA_IDLE_RK1808 (0x1 << 24) +#define CIF_MIPI_LVDS_SW_DMA_IDLE (0x1 << 16) +#define CIF_MIPI_LVDS_SW_PRESS_VALUE(val) (((val) & 0x3) << 13) +#define CIF_MIPI_LVDS_SW_PRESS_VALUE_RK3588(val) (((val) & 0x7) << 13) +#define CIF_MIPI_LVDS_SW_PRESS_ENABLE (0x1 << 12) +#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_8BITS (0x0 << 9) +#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_10BITS (0x1 << 9) +#define CIF_MIPI_LVDS_SW_LVDS_WIDTH_12BITS (0x2 << 9) +#define CIF_MIPI_LVDS_SW_SEL_LVDS (0x1 << 8) +#define CIF_MIPI_LVDS_SW_HURRY_VALUE(val) (((val) & 0x3) << 5) +#define CIF_MIPI_LVDS_SW_HURRY_VALUE_RK3588(val) (((val) & 0x7) << 5) +#define CIF_MIPI_LVDS_SW_HURRY_ENABLE (0x1 << 4) +#define CIF_MIPI_LVDS_SW_WATER_LINE_75 (0x0 << 1) +#define CIF_MIPI_LVDS_SW_WATER_LINE_50 (0x1 << 1) +#define CIF_MIPI_LVDS_SW_WATER_LINE_25 (0x2 << 1) +#define CIF_MIPI_LVDS_SW_WATER_LINE_00 (0x3 << 1) +#define CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE (0x1 << 0) +#define CIF_MIPI_LVDS_SW_DMA_IDLE_RK1808 (0x1 << 24) #define CIF_MIPI_LVDS_SW_HURRY_VALUE_RK1808(val) (((val) & 0x3) << 17) -#define CIF_MIPI_LVDS_SW_HURRY_ENABLE_RK1808 (0x1 << 16) -#define CIF_MIPI_LVDS_SW_WATER_LINE_75_RK1808 (0x0 << 0) -#define CIF_MIPI_LVDS_SW_WATER_LINE_50_RK1808 (0x1 << 0) -#define CIF_MIPI_LVDS_SW_WATER_LINE_25_RK1808 (0x2 << 0) -#define CIF_MIPI_LVDS_SW_WATER_LINE_00_RK1808 (0x3 << 0) +#define CIF_MIPI_LVDS_SW_HURRY_ENABLE_RK1808 (0x1 << 16) +#define CIF_MIPI_LVDS_SW_WATER_LINE_75_RK1808 (0x0 << 0) +#define CIF_MIPI_LVDS_SW_WATER_LINE_50_RK1808 (0x1 << 0) +#define CIF_MIPI_LVDS_SW_WATER_LINE_25_RK1808 (0x2 << 0) +#define CIF_MIPI_LVDS_SW_WATER_LINE_00_RK1808 (0x3 << 0) #define CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE_RK1808 (0x1 << 4) /* CSI Host Registers Define */ @@ -688,5 +958,16 @@ enum cif_reg_index { #define RK3568_CIF_PCLK_SAMPLING_EDGE_FALLING (0x10001000) #define RK3568_CIF_PCLK_SINGLE_EDGE (0x02000000) #define RK3568_CIF_PCLK_DUAL_EDGE (0x02000200) +#define CIF_GRF_SOC_CON2 (0x308) +#define RK3588_CIF_PCLK_SAMPLING_EDGE_RISING (0x00100000) +#define RK3588_CIF_PCLK_SAMPLING_EDGE_FALLING (0x00100010) +#define RK3588_CIF_PCLK_SINGLE_EDGE (0x00200000) +#define RK3588_CIF_PCLK_DUAL_EDGE (0x00200020) + + +/*toisp*/ +#define TOISP_END_CH0(index) (0x1 << (20 + index * 3)) +#define TOISP_END_CH1(index) (0x1 << (21 + index * 3)) +#define TOISP_END_CH2(index) (0x1 << (22 + index * 3)) #endif diff --git a/drivers/media/platform/rockchip/cif/subdev-itf.c b/drivers/media/platform/rockchip/cif/subdev-itf.c index e685fdc637c4..c00fb6e86ffb 100644 --- a/drivers/media/platform/rockchip/cif/subdev-itf.c +++ b/drivers/media/platform/rockchip/cif/subdev-itf.c @@ -20,12 +20,36 @@ #include #include #include "dev.h" +#include static inline struct sditf_priv *to_sditf_priv(struct v4l2_subdev *subdev) { return container_of(subdev, struct sditf_priv, sd); } +static void sditf_get_hdr_mode(struct sditf_priv *priv) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + struct rkmodule_hdr_cfg hdr_cfg; + int ret = 0; + + if (!cif_dev->terminal_sensor.sd) + rkcif_update_sensor_info(&cif_dev->stream[0]); + + if (cif_dev->terminal_sensor.sd) { + ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, + core, ioctl, + RKMODULE_GET_HDR_CFG, + &hdr_cfg); + if (!ret) + priv->hdr_cfg = hdr_cfg; + else + priv->hdr_cfg.hdr_mode = NO_HDR; + } else { + priv->hdr_cfg.hdr_mode = NO_HDR; + } +} + static int sditf_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *fi) { @@ -69,12 +93,15 @@ static int sditf_get_set_fmt(struct v4l2_subdev *sd, struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev_selection input_sel; + struct v4l2_pix_format_mplane pixm; int ret = -EINVAL; if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { + sditf_get_hdr_mode(priv); + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, fmt); if (ret) { v4l2_err(&priv->sd, @@ -83,6 +110,8 @@ static int sditf_get_set_fmt(struct v4l2_subdev *sd, } input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; + input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + input_sel.pad = 0; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); @@ -90,11 +119,57 @@ static int sditf_get_set_fmt(struct v4l2_subdev *sd, fmt->format.width = input_sel.r.width; fmt->format.height = input_sel.r.height; } + priv->cap_info.width = fmt->format.width; + priv->cap_info.height = fmt->format.height; + pixm.pixelformat = rkcif_mbus_pixelcode_to_v4l2(fmt->format.code); + pixm.width = priv->cap_info.width; + pixm.height = priv->cap_info.height; + v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, + "%s, width %d, height %d, hdr mode %d\n", + __func__, fmt->format.width, fmt->format.width, priv->hdr_cfg.hdr_mode); + if (priv->hdr_cfg.hdr_mode == NO_HDR) { + rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); + } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { + rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); + rkcif_set_fmt(&cif_dev->stream[1], &pixm, false); + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); + rkcif_set_fmt(&cif_dev->stream[1], &pixm, false); + rkcif_set_fmt(&cif_dev->stream[2], &pixm, false); + } } return 0; } +static int sditf_init_buf(struct sditf_priv *priv) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + int ret = 0; + + if (priv->hdr_cfg.hdr_mode == HDR_X2) { + ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); + ret |= rkcif_init_rx_buf(&cif_dev->stream[1], priv->buf_num); + } else { + ret = -EINVAL; + } + return ret; +} + +static void sditf_free_buf(struct sditf_priv *priv) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + + if (priv->hdr_cfg.hdr_mode == HDR_X2) { + rkcif_free_rx_buf(&cif_dev->stream[0], priv->buf_num); + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + rkcif_free_rx_buf(&cif_dev->stream[0], priv->buf_num); + rkcif_free_rx_buf(&cif_dev->stream[1], priv->buf_num); + } +} + static int sditf_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) @@ -102,18 +177,49 @@ static int sditf_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static void sditf_reinit_mode(struct sditf_priv *priv, struct rkisp_vicap_mode *mode) +{ + if (mode->is_rdbk) { + priv->toisp_inf.link_mode = TOISP_NONE; + } else { + if (strstr(mode->name, RKISP0_DEVNAME)) + priv->toisp_inf.link_mode = TOISP0; + else if (strstr(mode->name, RKISP1_DEVNAME)) + priv->toisp_inf.link_mode = TOISP1; + else if (strstr(mode->name, RKISP_UNITE_DEVNAME)) + priv->toisp_inf.link_mode = TOISP_UNITE; + else + v4l2_err(&priv->cif_dev->v4l2_dev, + "%s, mode name err, mode name: %s\n", + __func__, mode->name); + } + v4l2_dbg(3, rkcif_debug, &priv->cif_dev->v4l2_dev, + "%s, mode->is_rdbk %d, mode->name %s, link_mode %d\n", + __func__, mode->is_rdbk, mode->name, priv->toisp_inf.link_mode); +} + static long sditf_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct sditf_priv *priv = to_sditf_priv(sd); - struct rkcif_device *cif_dev = priv->cif_dev; - struct v4l2_subdev *sensor_sd; + struct rkisp_vicap_mode *mode; + struct v4l2_subdev_format fmt; + int *pbuf_num = NULL; + int ret = 0; - if (!cif_dev->terminal_sensor.sd) - rkcif_update_sensor_info(&cif_dev->stream[0]); - - if (cif_dev->terminal_sensor.sd) { - sensor_sd = cif_dev->terminal_sensor.sd; - return v4l2_subdev_call(sensor_sd, core, ioctl, cmd, arg); + switch (cmd) { + case RKISP_VICAP_CMD_MODE: + mode = (struct rkisp_vicap_mode *)arg; + memcpy(&priv->mode, mode, sizeof(*mode)); + sditf_reinit_mode(priv, &priv->mode); + return 0; + case RKISP_VICAP_CMD_INIT_BUF: + pbuf_num = (int *)arg; + priv->buf_num = *pbuf_num; + sditf_get_set_fmt(&priv->sd, NULL, &fmt); + ret = sditf_init_buf(priv); + return ret; + default: + break; } return -EINVAL; @@ -123,9 +229,36 @@ static long sditf_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) static long sditf_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) { + void __user *up = compat_ptr(arg); struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev *sensor_sd; + struct rkisp_vicap_mode *mode; + int buf_num; + int ret = 0; + + switch (cmd) { + case RKISP_VICAP_CMD_MODE: + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) { + ret = -ENOMEM; + return ret; + } + if (copy_from_user(mode, up, sizeof(*mode))) { + kfree(mode); + return -EFAULT; + } + ret = sditf_ioctl(sd, cmd, mode); + kfree(mode); + return ret; + case RKISP_VICAP_CMD_INIT_BUF: + if (copy_from_user(&buf_num, up, sizeof(int))) + return -EFAULT; + ret = sditf_ioctl(sd, cmd, &buf_num); + return ret; + default: + break; + } if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); @@ -139,6 +272,180 @@ static long sditf_compat_ioctl32(struct v4l2_subdev *sd, } #endif +static int sditf_channel_enable(struct sditf_priv *priv, int user) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + unsigned int ch0 = 0, ch1 = 0, ch2 = 0; + unsigned int ctrl_val = 0; + unsigned int int_en = 0; + unsigned int offset_x = 0; + unsigned int offset_y = 0; + unsigned int width = priv->cap_info.width; + unsigned int height = priv->cap_info.height; + + if (priv->hdr_cfg.hdr_mode == NO_HDR) { + if (cif_dev->inf_id == RKCIF_MIPI_LVDS) + ch0 = cif_dev->csi_host_idx * 4; + else + ch0 = 24;//dvp + ctrl_val = (ch0 << 3) | 0x1; + if (user == 0) + int_en = 0x04104003; + else + int_en = 0x08820003; + priv->toisp_inf.ch_info[0].is_valid = true; + priv->toisp_inf.ch_info[0].id = ch0; + } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { + ch0 = cif_dev->csi_host_idx * 4 + 1; + ch1 = cif_dev->csi_host_idx * 4; + ctrl_val = (ch0 << 3) | 0x1; + ctrl_val |= (ch1 << 11) | 0x100; + if (user == 0) + int_en = 0x0430c003; + else + int_en = 0x09860003; + priv->toisp_inf.ch_info[0].is_valid = true; + priv->toisp_inf.ch_info[0].id = ch0; + priv->toisp_inf.ch_info[1].is_valid = true; + priv->toisp_inf.ch_info[1].id = ch1; + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + ch0 = cif_dev->csi_host_idx * 4 + 2; + ch1 = cif_dev->csi_host_idx * 4 + 1; + ch2 = cif_dev->csi_host_idx * 4; + ctrl_val = (ch0 << 3) | 0x1; + ctrl_val |= (ch1 << 11) | 0x100; + ctrl_val |= (ch2 << 19) | 0x10000; + if (user == 0) + int_en = 0x0471c003; + else + int_en = 0x0b8e0003; + priv->toisp_inf.ch_info[0].is_valid = true; + priv->toisp_inf.ch_info[0].id = ch0; + priv->toisp_inf.ch_info[1].is_valid = true; + priv->toisp_inf.ch_info[1].id = ch1; + priv->toisp_inf.ch_info[2].is_valid = true; + priv->toisp_inf.ch_info[2].id = ch2; + } + if (user == 0) { + if (priv->toisp_inf.link_mode == TOISP_UNITE) + width = priv->cap_info.width / 2 + 256; + rkcif_write_register_or(cif_dev, CIF_REG_TOISP0_CTRL, ctrl_val); + if (width && height) { + rkcif_write_register(cif_dev, CIF_REG_TOISP0_CROP, + offset_x | (offset_y << 16)); + rkcif_write_register(cif_dev, CIF_REG_TOISP0_SIZE, + width | (height << 16)); + } else { + return -EINVAL; + } + rkcif_write_register_or(cif_dev, CIF_REG_GLB_INTEN, int_en); + } else { + if (priv->toisp_inf.link_mode == TOISP_UNITE) { + offset_x = priv->cap_info.width / 2 - 256; + width = priv->cap_info.width / 2 + 256; + } + rkcif_write_register_or(cif_dev, CIF_REG_TOISP1_CTRL, ctrl_val); + if (width && height) { + rkcif_write_register(cif_dev, CIF_REG_TOISP1_CROP, + offset_x | (offset_y << 16)); + rkcif_write_register(cif_dev, CIF_REG_TOISP1_SIZE, + width | (height << 16)); + } else { + return -EINVAL; + } + rkcif_write_register_or(cif_dev, CIF_REG_GLB_INTEN, int_en); + } + return 0; +} + +static __maybe_unused void sditf_channel_disable(struct sditf_priv *priv, int user) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + unsigned int ctrl_val = 0; + + if (priv->hdr_cfg.hdr_mode == NO_HDR) + ctrl_val = 0x01; + else if (priv->hdr_cfg.hdr_mode == HDR_X2) + ctrl_val = 0x101; + else if (priv->hdr_cfg.hdr_mode == HDR_X3) + ctrl_val = 0x10101; + if (user == 0) + rkcif_write_register_and(cif_dev, CIF_REG_TOISP0_CTRL, ~ctrl_val); + else + rkcif_write_register_and(cif_dev, CIF_REG_TOISP1_CTRL, ~ctrl_val); + priv->toisp_inf.ch_info[0].is_valid = false; + priv->toisp_inf.ch_info[1].is_valid = false; + priv->toisp_inf.ch_info[2].is_valid = false; +} + +static int sditf_start_stream(struct sditf_priv *priv) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + struct v4l2_subdev_format fmt; + + sditf_get_set_fmt(&priv->sd, NULL, &fmt); + if (priv->toisp_inf.link_mode == TOISP0) { + sditf_channel_enable(priv, 0); + } else if (priv->toisp_inf.link_mode == TOISP1) { + sditf_channel_enable(priv, 1); + } else if (priv->toisp_inf.link_mode == TOISP_UNITE) { + sditf_channel_enable(priv, 0); + sditf_channel_enable(priv, 1); + } + + if (priv->hdr_cfg.hdr_mode == NO_HDR) { + rkcif_do_start_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { + rkcif_do_start_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + rkcif_do_start_stream(&cif_dev->stream[1], RKCIF_STREAM_MODE_TOISP); + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + rkcif_do_start_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + rkcif_do_start_stream(&cif_dev->stream[1], RKCIF_STREAM_MODE_TOISP); + rkcif_do_start_stream(&cif_dev->stream[2], RKCIF_STREAM_MODE_TOISP); + } + return 0; +} + +static int sditf_stop_stream(struct sditf_priv *priv) +{ + struct rkcif_device *cif_dev = priv->cif_dev; + + if (priv->hdr_cfg.hdr_mode == NO_HDR) { + rkcif_do_stop_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { + rkcif_do_stop_stream(&cif_dev->stream[1], RKCIF_STREAM_MODE_TOISP); + rkcif_do_stop_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { + rkcif_do_stop_stream(&cif_dev->stream[2], RKCIF_STREAM_MODE_TOISP); + rkcif_do_stop_stream(&cif_dev->stream[1], RKCIF_STREAM_MODE_TOISP); + rkcif_do_stop_stream(&cif_dev->stream[0], RKCIF_STREAM_MODE_TOISP); + } + return 0; +} + +static int sditf_s_stream(struct v4l2_subdev *sd, int on) +{ + struct sditf_priv *priv = to_sditf_priv(sd); + struct rkcif_device *cif_dev = priv->cif_dev; + int ret = 0; + + if (cif_dev->chip_id >= CHIP_RK3588_CIF) { + if (priv->toisp_inf.link_mode == TOISP_NONE) + return 0; + v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, + "%s, toisp mode %d, hdr %d, stream on %d\n", + __func__, priv->toisp_inf.link_mode, priv->hdr_cfg.hdr_mode, on); + if (on) { + ret = sditf_start_stream(priv); + } else { + ret = sditf_stop_stream(priv); + sditf_free_buf(priv); + } + + } + return ret; +} + static const struct v4l2_subdev_pad_ops sditf_subdev_pad_ops = { .set_fmt = sditf_get_set_fmt, .get_fmt = sditf_get_set_fmt, @@ -148,6 +455,7 @@ static const struct v4l2_subdev_pad_ops sditf_subdev_pad_ops = { static const struct v4l2_subdev_video_ops sditf_video_ops = { .g_frame_interval = sditf_g_frame_interval, + .s_stream = sditf_s_stream, }; static const struct v4l2_subdev_core_ops sditf_core_ops = { @@ -206,7 +514,13 @@ static int rkcif_subdev_media_init(struct sditf_priv *priv) return ret; strncpy(priv->sd.name, dev_name(cif_dev->dev), sizeof(priv->sd.name)); - + priv->cap_info.width = 0; + priv->cap_info.height = 0; + priv->mode.is_rdbk = 0; + priv->toisp_inf.link_mode = TOISP_NONE; + priv->toisp_inf.ch_info[0].is_valid = false; + priv->toisp_inf.ch_info[1].is_valid = false; + priv->toisp_inf.ch_info[2].is_valid = false; return 0; } diff --git a/drivers/media/platform/rockchip/cif/subdev-itf.h b/drivers/media/platform/rockchip/cif/subdev-itf.h index f81b69dc56f6..c432fbc4bf0a 100644 --- a/drivers/media/platform/rockchip/cif/subdev-itf.h +++ b/drivers/media/platform/rockchip/cif/subdev-itf.h @@ -17,12 +17,51 @@ #include #include #include "hw.h" +#include "../isp/isp_external.h" + +#define RKISP0_DEVNAME "rkisp0" +#define RKISP1_DEVNAME "rkisp1" +#define RKISP_UNITE_DEVNAME "rkisp_unite" + +#define RKCIF_TOISP_CH0 0 +#define RKCIF_TOISP_CH1 1 +#define RKCIF_TOISP_CH2 2 +#define TOISP_CH_MAX 3 + +struct capture_info { + unsigned int offset_x; + unsigned int offset_y; + unsigned int width; + unsigned int height; +}; + +enum toisp_link_mode { + TOISP_NONE, + TOISP0, + TOISP1, + TOISP_UNITE, +}; + +struct toisp_ch_info { + bool is_valid; + int id; +}; + +struct toisp_info { + struct toisp_ch_info ch_info[TOISP_CH_MAX]; + enum toisp_link_mode link_mode; +}; struct sditf_priv { struct device *dev; struct v4l2_subdev sd; struct media_pad pads; struct rkcif_device *cif_dev; + struct rkmodule_hdr_cfg hdr_cfg; + struct capture_info cap_info; + struct rkisp_vicap_mode mode; + struct toisp_info toisp_inf; + int buf_num; }; extern struct platform_driver rkcif_subdev_driver; diff --git a/include/uapi/linux/rkcif-config.h b/include/uapi/linux/rkcif-config.h index 0fa5f040f321..1d57aa3a6432 100644 --- a/include/uapi/linux/rkcif-config.h +++ b/include/uapi/linux/rkcif-config.h @@ -17,6 +17,12 @@ #define RKCIF_CMD_SET_CSI_MEMORY_MODE \ _IOW('V', BASE_VIDIOC_PRIVATE + 1, int) +#define RKCIF_CMD_GET_SCALE_BLC \ + _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct bayer_blc) + +#define RKCIF_CMD_SET_SCALE_BLC \ + _IOW('V', BASE_VIDIOC_PRIVATE + 3, struct bayer_blc) + /* cif memory mode * 0: raw12/raw10/raw8 8bit memory compact * 1: raw12/raw10 16bit memory one pixel @@ -38,4 +44,14 @@ enum cif_csi_lvds_memory { CSI_LVDS_MEM_WORD_HIGH_ALIGN = 2, }; +/* black level for scale image + * The sequence of pattern00~03 is the same as the output of sensor bayer + */ + +struct bayer_blc { + u8 pattern00; + u8 pattern01; + u8 pattern02; + u8 pattern03; +}; #endif