From 573f0547a89be335652d76b0d6e0f66ab63d576c Mon Sep 17 00:00:00 2001 From: Wenlong Zhuang Date: Fri, 28 Sep 2018 21:07:25 +0800 Subject: [PATCH] media: rockchip/cif: support capture mipi csi data for rk1808 Also add support pingpong frame mode. Change-Id: Ibc29e3452351ae6ffafebbf72386b47bfb853f8c Signed-off-by: Wenlong Zhuang --- drivers/media/platform/rockchip/cif/capture.c | 542 +++++++++++++++++- drivers/media/platform/rockchip/cif/dev.c | 74 ++- drivers/media/platform/rockchip/cif/dev.h | 43 +- drivers/media/platform/rockchip/cif/regs.h | 121 ++++ 4 files changed, 763 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index c3ebec506f02..7121ff6bc5f9 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -83,6 +83,81 @@ static const struct cif_output_fmt out_fmts[] = { .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .cplanes = 1, + .mplanes = 1, + .bpp = { 24 }, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_BGR666, + .cplanes = 1, + .mplanes = 1, + .bpp = { 18 }, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, }, /* TODO: We can support NV12M/NV21M/NV16M/NV61M too */ @@ -105,6 +180,45 @@ static const struct cif_input_fmt in_fmts[] = { .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt_val = CSI_WRDDR_TYPE_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .fmt_val = CSI_WRDDR_TYPE_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt_val = CSI_WRDDR_TYPE_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .fmt_val = CSI_WRDDR_TYPE_RAW8, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt_val = CSI_WRDDR_TYPE_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt_val = CSI_WRDDR_TYPE_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt_val = CSI_WRDDR_TYPE_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt_val = CSI_WRDDR_TYPE_RAW10, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt_val = CSI_WRDDR_TYPE_RAW12, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt_val = CSI_WRDDR_TYPE_RAW12, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt_val = CSI_WRDDR_TYPE_RAW12, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt_val = CSI_WRDDR_TYPE_RAW12, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fmt_val = CSI_WRDDR_TYPE_RGB888, }, }; @@ -132,6 +246,32 @@ static struct rkcif_sensor_info *get_active_sensor(struct rkcif_stream *stream) return NULL; } +static unsigned char get_data_type(u32 pixelformat, u8 cmd_mode_en) +{ + switch (pixelformat) { + /* csi raw8 */ + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SBGGR8: + return 0x2a; + /* csi raw10 */ + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SBGGR10: + return 0x2b; + case V4L2_PIX_FMT_RGB24: { + if (cmd_mode_en) /* dsi command mode*/ + return 0x39; + else /* dsi video mode */ + return 0x3e; + } + default: + return 0x2b; + } +} + static const struct cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect) { @@ -221,11 +361,306 @@ static void rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream) } } +static void rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream, + int init, int csi_ch) +{ + struct rkcif_dummy_buffer *dummy_buf = &stream->dummy_buf; + struct rkcif_device *dev = stream->cifdev; + struct rkcif_buffer *buffer = NULL; + void __iomem *base = dev->base_addr; + u32 frm_addr_y, frm_addr_uv; + + /* Set up an empty buffer for the next frame */ + spin_lock(&stream->vbq_lock); + if (!list_empty(&stream->buf_head)) { + if (stream->frame_phase == 0 || init) { + stream->curr_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->curr_buf->queue); + buffer = stream->curr_buf; + } + if (stream->frame_phase == 1 || init) { + stream->next_buf = list_first_entry(&stream->buf_head, + struct rkcif_buffer, queue); + list_del(&stream->next_buf->queue); + buffer = stream->next_buf; + } + } else { + if (stream->frame_phase == 0) + stream->curr_buf = NULL; + if (stream->frame_phase == 1) + stream->next_buf = NULL; + + buffer = NULL; + } + spin_unlock(&stream->vbq_lock); + + if (init) { + u32 frm0_addr_y, frm0_addr_uv; + u32 frm1_addr_y, frm1_addr_uv; + + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + frm0_addr_y = CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch; + frm0_addr_uv = CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch; + frm1_addr_y = CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch; + frm1_addr_uv = CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch; + } else { + frm0_addr_y = CIF_FRM0_ADDR_Y; + frm0_addr_uv = CIF_FRM0_ADDR_UV; + frm1_addr_y = CIF_FRM1_ADDR_Y; + frm1_addr_uv = CIF_FRM1_ADDR_UV; + } + + if (stream->curr_buf && stream->next_buf) { + write_cif_reg(base, frm0_addr_y, + stream->curr_buf->buff_addr[RKCIF_PLANE_Y]); + write_cif_reg(base, frm0_addr_uv, + stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]); + write_cif_reg(base, frm1_addr_y, + stream->next_buf->buff_addr[RKCIF_PLANE_Y]); + write_cif_reg(base, frm1_addr_uv, + stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]); + } else { + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n"); + write_cif_reg(base, frm0_addr_y, dummy_buf->dma_addr); + write_cif_reg(base, frm0_addr_uv, dummy_buf->dma_addr); + write_cif_reg(base, frm1_addr_y, dummy_buf->dma_addr); + write_cif_reg(base, frm1_addr_uv, dummy_buf->dma_addr); + } + + return; + } + + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + frm_addr_y = stream->frame_phase == 1 ? + (CIF_CSI_FRM1_ADDR_Y_ID0 + 0x20 * csi_ch) : + (CIF_CSI_FRM0_ADDR_Y_ID0 + 0x20 * csi_ch); + frm_addr_uv = stream->frame_phase == 1 ? + (CIF_CSI_FRM1_ADDR_UV_ID0 + 0x20 * csi_ch) : + (CIF_CSI_FRM0_ADDR_UV_ID0 + 0x20 * csi_ch); + } else { + frm_addr_y = stream->frame_phase == 1 ? + CIF_FRM1_ADDR_Y : CIF_FRM0_ADDR_Y; + frm_addr_uv = stream->frame_phase == 1 ? + CIF_FRM1_ADDR_UV : CIF_FRM0_ADDR_UV; + } + + if (buffer) { + write_cif_reg(base, frm_addr_y, + buffer->buff_addr[RKCIF_PLANE_Y]); + write_cif_reg(base, frm_addr_uv, + buffer->buff_addr[RKCIF_PLANE_CBCR]); + } else { + v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "Drop to dummy buf\n"); + write_cif_reg(base, frm_addr_y, dummy_buf->dma_addr); + write_cif_reg(base, frm_addr_uv, dummy_buf->dma_addr); + } +} + +static void rkcif_csihost_disable(struct rkcif_device *dev) +{ + void __iomem *base = dev->csi_base; + + write_csihost_reg(base, CSIHOST_RESETN, 0); + + v4l2_info(&dev->v4l2_dev, "mipi csi host disable\n"); +} + +static void rkcif_csihost_enable(struct rkcif_device *dev, + enum host_type_t host_type, int lanes) +{ + void __iomem *base = dev->csi_base; + + 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)); + } else { + write_csihost_reg(base, CSIHOST_CONTROL, + SW_CPHY_EN(0) | SW_DSI_EN(0)); + } + + write_csihost_reg(base, CSIHOST_RESETN, 1); + + v4l2_info(&dev->v4l2_dev, "mipi csi host enable\n"); +} + +static int rkcif_csi_crop_check(struct rkcif_device *dev, + struct csi_channel_info *channel) +{ + switch (channel->fmt_val) { + case CSI_WRDDR_TYPE_RGB888: + if (channel->crop_st_x % 24 != 0) { + channel->crop_st_x = ALIGN(channel->crop_st_x, 24); + v4l2_info(&dev->v4l2_dev, + "align 24 pixel: crop_st_x change to %d", + channel->crop_st_x); + } + channel->width -= channel->crop_st_x / 3; + break; + case CSI_WRDDR_TYPE_RAW10: + case CSI_WRDDR_TYPE_RAW12: + if (channel->crop_st_x % 4 != 0) { + channel->crop_st_x = ALIGN(channel->crop_st_x, 4); + v4l2_info(&dev->v4l2_dev, + "align 4 pixel: crop_st_x change to %d", + channel->crop_st_x); + } + channel->width -= channel->crop_st_x; + break; + case CSI_WRDDR_TYPE_RAW8: + case CSI_WRDDR_TYPE_YUV422: + if (channel->crop_st_x % 8 != 0) { + channel->crop_st_x = ALIGN(channel->crop_st_x, 8); + v4l2_info(&dev->v4l2_dev, + "align 8 pixel: crop_st_x change to %d", + channel->crop_st_x); + } + channel->width -= channel->crop_st_x; + break; + default: + break; + } + + channel->height -= channel->crop_st_y; + + return 0; +} + +static int rkcif_csi_channel_init(struct rkcif_stream *stream, + struct csi_channel_info *channel) +{ + struct rkcif_device *dev = stream->cifdev; + struct rkcif_sensor_info *sensor_info = dev->active_sensor; + const struct cif_output_fmt *fmt; + u32 mbus_flags = sensor_info->mbus.flags; + + channel->enable = 1; + channel->width = stream->pixm.width; + channel->height = stream->pixm.height; + channel->fmt_val = stream->cif_fmt_in->fmt_val; + channel->cmd_mode_en = 0; /* default use DSI Video Mode */ + channel->crop_en = 1; /* Force crop enable to avoid write overflow */ + channel->crop_st_x = stream->crop.left; + channel->crop_st_y = stream->crop.top; + if (channel->crop_en) + rkcif_csi_crop_check(dev, channel); + + fmt = find_output_fmt(stream, stream->pixm.pixelformat); + if (!fmt) { + v4l2_err(&dev->v4l2_dev, "can not find output format: 0x%x", + stream->pixm.pixelformat); + return -EINVAL; + } + + channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8); + + /* TODO Modify data type !!!!!!!!!!!!!!! */ + channel->data_type = + get_data_type(stream->pixm.pixelformat, channel->cmd_mode_en); + + if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_0) + channel->vc = 0; + else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_1) + channel->vc = 1; + else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_2) + channel->vc = 2; + else if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_3) + channel->vc = 3; + + return 0; +} + +static int rkcif_csi_channel_set(struct rkcif_stream *stream, + struct csi_channel_info *channel) +{ + struct rkcif_device *dev = stream->cifdev; + void __iomem *base = dev->base_addr; + unsigned int val; + + if (channel->id >= 4) + return -EINVAL; + + if (!channel->enable) { + write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * channel->id, + CSI_DISABLE_CAPTURE); + return 0; + } + + write_cif_reg(base, CIF_CSI_ID0_CTRL1 + 0x8 * channel->id, + channel->width | (channel->height << 16)); + write_cif_reg(base, CIF_CSI_FRM0_VLW_Y_ID0 + 0x20 * channel->id, + channel->virtual_width); + write_cif_reg(base, CIF_CSI_FRM1_VLW_Y_ID0 + 0x20 * channel->id, + channel->virtual_width); + + if (channel->crop_en) + write_cif_reg(base, CIF_CSI_ID0_CROP_START + 0x4 * channel->id, + channel->crop_st_y << 16 | channel->crop_st_x); + + /* Set up an buffer for the next frame */ + rkcif_assign_new_buffer_pingpong(stream, 1, channel->id); + + val = CSI_ENABLE_CAPTURE | channel->fmt_val | + channel->cmd_mode_en << 4 | channel->crop_en << 5 | + channel->vc << 8 | channel->data_type << 10; + write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * channel->id, val); + + return 0; +} + +static int rkcif_csi_stream_start(struct rkcif_stream *stream) +{ + struct rkcif_device *dev = stream->cifdev; + struct rkcif_sensor_info *sensor_info = dev->active_sensor; + void __iomem *base = dev->base_addr; + enum host_type_t host_type; + int i; + + stream->frame_idx = 0; + + /* TODO Modify to support multiple channel */ + for (i = 0; i < RKCIF_MAX_CSI_CHANNEL; i++) { + struct csi_channel_info *channel = &dev->channels[i]; + + channel->id = i; + + if (i < dev->num_channels) + rkcif_csi_channel_init(stream, channel); + else + channel->enable = 0; + + rkcif_csi_channel_set(stream, channel); + } + + write_cif_reg(base, CIF_CSI_INTSTAT, 0x0); + write_cif_reg(base, CIF_CSI_INTEN, + CSI_ALL_FRAME_START_INTEN | + CSI_ALL_FRAME_END_INTEN | + CSI_ALL_ERROR_INTEN); + + /* enable csi host */ + if (stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_RGB888_1X24) + host_type = RK_DSI_RXHOST; + else + host_type = RK_CSI_RXHOST; + + rkcif_csihost_enable(dev, host_type, sensor_info->lanes); + + stream->state = RKCIF_STATE_STREAMING; + + return 0; +} + static void rkcif_stream_stop(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; void __iomem *base = cif_dev->base_addr; u32 val; + int i; val = read_cif_reg(base, CIF_CTRL); write_cif_reg(base, CIF_CTRL, val & (~ENABLE_CAPTURE)); @@ -233,6 +668,21 @@ static void rkcif_stream_stop(struct rkcif_stream *stream) write_cif_reg(base, CIF_INTSTAT, 0x3ff); write_cif_reg(base, CIF_FRAME_STATUS, 0x0); + if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + /* disable csi host */ + rkcif_csihost_disable(cif_dev); + + /* disable cif csi */ + for (i = 0; i < 4; i++) { + val = read_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * i); + write_cif_reg(base, CIF_CSI_ID0_CTRL0 + 0x8 * i, + (val & (~CSI_ENABLE_CAPTURE))); + } + + write_cif_reg(base, CIF_CSI_INTEN, 0x0); + write_cif_reg(base, CIF_CSI_INTSTAT, 0x1ffffff); + } + stream->state = RKCIF_STATE_READY; } @@ -378,6 +828,11 @@ static void rkcif_stop_streaming(struct vb2_queue *queue) list_add_tail(&stream->curr_buf->queue, &stream->buf_head); stream->curr_buf = NULL; } + if (stream->next_buf) { + list_add_tail(&stream->next_buf->queue, &stream->buf_head); + stream->next_buf = NULL; + } + while (!list_empty(&stream->buf_head)) { buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); @@ -536,7 +991,13 @@ static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) v4l2_err(v4l2_dev, "Failed to get runtime pm, %d\n", ret); goto destroy_dummy_buf; } - ret = rkcif_stream_start(stream); + + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + dev->num_channels = 1; + ret = rkcif_csi_stream_start(stream); + } else { + ret = rkcif_stream_start(stream); + } if (ret < 0) goto runtime_put; @@ -844,7 +1305,7 @@ int rkcif_register_stream_vdev(struct rkcif_device *dev) struct video_device *vdev = &stream->vdev; int ret; - strlcpy(vdev->name, "stream_cif", sizeof(vdev->name)); + strlcpy(vdev->name, CIF_VIDEODEVICE_NAME, sizeof(vdev->name)); mutex_init(&stream->vlock); vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops; @@ -968,5 +1429,80 @@ void rkcif_irq_oneframe(struct rkcif_device *cif_dev) void rkcif_irq_pingpong(struct rkcif_device *cif_dev) { - /* TODO */ + struct rkcif_stream *stream = &cif_dev->stream; + void __iomem *base = cif_dev->base_addr; + unsigned int intstat; + + if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { + intstat = read_cif_reg(base, CIF_CSI_INTSTAT); + /* clear all interrupts that has been triggered */ + write_cif_reg(base, CIF_CSI_INTSTAT, intstat); + + if (intstat & CSI_FIFO_OVERFLOW) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: csi fifo overflow!! intstat: 0x%x\n", + intstat); + return; + } + + if (intstat & CSI_BANDWIDTH_LACK) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: csi bandwidth lack!!\n"); + return; + } + + if (intstat & CSI_FRAME0_END_ID0 && + intstat & CSI_FRAME1_END_ID0) { + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: both frame0 and frame1 int\n"); + return; + } + + if (intstat & CSI_FRAME0_END_ID0 || + intstat & CSI_FRAME1_END_ID0) { + struct vb2_v4l2_buffer *vb_done = NULL; + + if (stream->stopping) { + rkcif_stream_stop(stream); + stream->stopping = false; + wake_up(&stream->wq_stopped); + return; + } + + if (stream->frame_idx == 0) + stream->frame_phase = + intstat & CSI_FRAME0_END_ID0 ? 0 : 1; + else + stream->frame_phase ^= 1; + + if (intstat & CSI_FRAME0_END_ID0 && + stream->frame_phase != 0) { + stream->frame_phase = 0; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: last frame1 intr miss\n"); + } + if (intstat & CSI_FRAME1_END_ID0 && + stream->frame_phase != 1) { + stream->frame_phase = 1; + v4l2_err(&cif_dev->v4l2_dev, + "ERROR: last frame0 intr miss\n"); + } + + if (stream->frame_phase == 1) { + if (stream->next_buf) + vb_done = &stream->next_buf->vb; + } else { + if (stream->curr_buf) + vb_done = &stream->curr_buf->vb; + } + rkcif_assign_new_buffer_pingpong(stream, 0, 0); + + if (vb_done) + rkcif_vb_done_oneframe(stream, vb_done); + + stream->frame_idx++; + } + } else { + /* TODO */ + } } diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c index 9d6dd2b7ecfc..ddefd4dd7356 100644 --- a/drivers/media/platform/rockchip/cif/dev.c +++ b/drivers/media/platform/rockchip/cif/dev.c @@ -23,6 +23,7 @@ #include "regs.h" struct cif_match_data { + int chip_id; const char * const *clks; const char * const *rsts; int clks_num; @@ -96,6 +97,7 @@ unlock: struct rkcif_async_subdev { struct v4l2_async_subdev asd; struct v4l2_mbus_config mbus; + int lanes; }; static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, @@ -110,6 +112,7 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, if (cif_dev->num_sensors == ARRAY_SIZE(cif_dev->sensors)) return -EBUSY; + cif_dev->sensors[cif_dev->num_sensors].lanes = s_asd->lanes; cif_dev->sensors[cif_dev->num_sensors].mbus = s_asd->mbus; cif_dev->sensors[cif_dev->num_sensors].sd = subdev; ++cif_dev->num_sensors; @@ -127,17 +130,20 @@ static int rkcif_fwnode_parse(struct device *dev, container_of(asd, struct rkcif_async_subdev, asd); struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel; - /* - * MIPI sensor is linked with a mipi dphy and its media bus config can - * not be get in here - */ if (vep->bus_type != V4L2_MBUS_BT656 && - vep->bus_type != V4L2_MBUS_PARALLEL) + vep->bus_type != V4L2_MBUS_PARALLEL && + vep->bus_type != V4L2_MBUS_CSI2) return 0; - rk_asd->mbus.flags = bus->flags; rk_asd->mbus.type = vep->bus_type; + if (vep->bus_type == V4L2_MBUS_CSI2) { + rk_asd->mbus.flags = vep->bus.mipi_csi2.flags; + rk_asd->lanes = vep->bus.mipi_csi2.num_data_lanes; + } else { + rk_asd->mbus.flags = bus->flags; + } + return 0; } @@ -199,6 +205,22 @@ static const char * const px30_cif_rsts[] = { "rst_cif_pclkin", }; +static const char * const rk1808_cif_clks[] = { + "aclk_cif", + "dclk_cif", + "hclk_cif", + "sclk_cif_out", + "pclk_csi2host" +}; + +static const char * const rk1808_cif_rsts[] = { + "rst_cif_a", + "rst_cif_h", + "rst_cif_i", + "rst_cif_d", + "rst_cif_pclkin", +}; + static const char * const rk3128_cif_clks[] = { "aclk_cif", "hclk_cif", @@ -220,21 +242,32 @@ static const char * const rk3288_cif_rsts[] = { "rst_cif", }; -static const struct cif_match_data px30_cif_clk_data = { +static const struct cif_match_data px30_cif_match_data = { + .chip_id = CHIP_PX30_CIF, .clks = px30_cif_clks, .clks_num = ARRAY_SIZE(px30_cif_clks), .rsts = px30_cif_rsts, .rsts_num = ARRAY_SIZE(px30_cif_rsts), }; -static const struct cif_match_data rk3128_cif_clk_data = { +static const struct cif_match_data rk1808_cif_match_data = { + .chip_id = CHIP_RK1808_CIF, + .clks = rk1808_cif_clks, + .clks_num = ARRAY_SIZE(rk1808_cif_clks), + .rsts = rk1808_cif_rsts, + .rsts_num = ARRAY_SIZE(rk1808_cif_rsts), +}; + +static const struct cif_match_data rk3128_cif_match_data = { + .chip_id = CHIP_RK3128_CIF, .clks = rk3128_cif_clks, .clks_num = ARRAY_SIZE(rk3128_cif_clks), .rsts = rk3128_cif_rsts, .rsts_num = ARRAY_SIZE(rk3128_cif_rsts), }; -static const struct cif_match_data rk3288_cif_clk_data = { +static const struct cif_match_data rk3288_cif_match_data = { + .chip_id = CHIP_RK3288_CIF, .clks = rk3288_cif_clks, .clks_num = ARRAY_SIZE(rk3288_cif_clks), .rsts = rk3288_cif_rsts, @@ -244,15 +277,19 @@ static const struct cif_match_data rk3288_cif_clk_data = { static const struct of_device_id rkcif_plat_of_match[] = { { .compatible = "rockchip,px30-cif", - .data = &px30_cif_clk_data, + .data = &px30_cif_match_data, + }, + { + .compatible = "rockchip,rk1808-cif", + .data = &rk1808_cif_match_data, }, { .compatible = "rockchip,rk3128-cif", - .data = &rk3128_cif_clk_data, + .data = &rk3128_cif_match_data, }, { .compatible = "rockchip,rk3288-cif", - .data = &rk3288_cif_clk_data, + .data = &rk3288_cif_match_data, }, {}, }; @@ -414,7 +451,7 @@ static int rkcif_plat_probe(struct platform_device *pdev) dev_set_drvdata(dev, cif_dev); cif_dev->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cif_regs"); cif_dev->base_addr = devm_ioremap_resource(dev, res); if (IS_ERR(cif_dev->base_addr)) return PTR_ERR(cif_dev->base_addr); @@ -432,6 +469,17 @@ static int rkcif_plat_probe(struct platform_device *pdev) cif_dev->irq = irq; data = match->data; + cif_dev->chip_id = data->chip_id; + if (data->chip_id == CHIP_RK1808_CIF) { + using_pingpong = 1; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csihost_regs"); + cif_dev->csi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(cif_dev->csi_base)) + return PTR_ERR(cif_dev->csi_base); + } else { + using_pingpong = 0; + } + if (data->clks_num > RKCIF_MAX_BUS_CLK || data->rsts_num > RKCIF_MAX_RESET) { dev_err(dev, "out of range: clks(%d %d) rsts(%d %d)\n", diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index 9891aaa4dd89..c61657adbcec 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -16,10 +16,12 @@ #include #define CIF_DRIVER_NAME "rkcif" +#define CIF_VIDEODEVICE_NAME "stream_cif" #define RKCIF_MAX_BUS_CLK 8 #define RKCIF_MAX_SENSOR 2 -#define RKCIF_MAX_RESET 3 +#define RKCIF_MAX_RESET 5 +#define RKCIF_MAX_CSI_CHANNEL 4 #define RKCIF_DEFAULT_WIDTH 640 #define RKCIF_DEFAULT_HEIGHT 480 @@ -27,12 +29,27 @@ #define write_cif_reg(base, addr, val) writel(val, (addr) + (base)) #define read_cif_reg(base, addr) readl((addr) + (base)) +#define write_csihost_reg(base, addr, val) writel(val, (addr) + (base)) +#define read_csihost_reg(base, addr) readl((addr) + (base)) + enum rkcif_state { RKCIF_STATE_DISABLED, RKCIF_STATE_READY, RKCIF_STATE_STREAMING }; +enum rkcif_chip_id { + CHIP_PX30_CIF, + CHIP_RK1808_CIF, + CHIP_RK3128_CIF, + CHIP_RK3288_CIF +}; + +enum host_type_t { + RK_CSI_RXHOST, + RK_DSI_RXHOST +}; + struct rkcif_buffer { struct vb2_v4l2_buffer vb; struct list_head queue; @@ -62,6 +79,7 @@ static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb) struct rkcif_sensor_info { struct v4l2_subdev *sd; struct v4l2_mbus_config mbus; + int lanes; }; /* @@ -97,6 +115,21 @@ struct cif_input_fmt { enum cif_fmt_type fmt_type; }; +struct csi_channel_info { + unsigned char id; + unsigned char enable; /* capture enable */ + unsigned char vc; + unsigned char data_type; + unsigned char crop_en; + unsigned char cmd_mode_en; + unsigned char fmt_val; + unsigned int width; + unsigned int height; + unsigned int virtual_width; + unsigned int crop_st_x; + unsigned int crop_st_y; +}; + /* * struct rkcif_stream - Stream states TODO * @@ -106,6 +139,7 @@ struct cif_input_fmt { * * rkcif use shadowsock registers, so it need two buffer at a time * @curr_buf: the buffer used for current frame + * @next_buf: the buffer used for next frame */ struct rkcif_stream { struct rkcif_device *cifdev; @@ -113,6 +147,7 @@ struct rkcif_stream { bool stopping; wait_queue_head_t wq_stopped; int frame_idx; + int frame_phase; /* lock between irq and buf_queue */ spinlock_t vbq_lock; @@ -120,6 +155,7 @@ struct rkcif_stream { struct list_head buf_head; struct rkcif_dummy_buffer dummy_buf; struct rkcif_buffer *curr_buf; + struct rkcif_buffer *next_buf; /* vfd lock */ struct mutex vlock; @@ -148,6 +184,7 @@ struct rkcif_device { struct device *dev; int irq; void __iomem *base_addr; + void __iomem *csi_base; struct clk *clks[RKCIF_MAX_BUS_CLK]; int clk_size; struct vb2_alloc_ctx *alloc_ctx; @@ -165,6 +202,10 @@ struct rkcif_device { struct rkcif_sensor_info *active_sensor; struct rkcif_stream stream; + + struct csi_channel_info channels[RKCIF_MAX_CSI_CHANNEL]; + int num_channels; + int chip_id; }; void rkcif_unregister_stream_vdev(struct rkcif_device *dev); diff --git a/drivers/media/platform/rockchip/cif/regs.h b/drivers/media/platform/rockchip/cif/regs.h index 8fa9b5c6cfc1..fae14d16444d 100644 --- a/drivers/media/platform/rockchip/cif/regs.h +++ b/drivers/media/platform/rockchip/cif/regs.h @@ -37,6 +37,59 @@ #define CIF_LAST_LINE 0x68 #define CIF_LAST_PIX 0x6c +/* RK1808 CIF CSI Registers Offset */ +#define CIF_CSI_ID0_CTRL0 0x80 +#define CIF_CSI_ID0_CTRL1 0x84 +#define CIF_CSI_ID1_CTRL0 0x88 +#define CIF_CSI_ID1_CTRL1 0x8c +#define CIF_CSI_ID2_CTRL0 0x90 +#define CIF_CSI_ID2_CTRL1 0x94 +#define CIF_CSI_ID3_CTRL0 0x98 +#define CIF_CSI_ID3_CTRL1 0x9c +#define CIF_CSI_WATER_LINE 0xa0 +#define CIF_CSI_FRM0_ADDR_Y_ID0 0xa4 +#define CIF_CSI_FRM1_ADDR_Y_ID0 0xa8 +#define CIF_CSI_FRM0_ADDR_UV_ID0 0xac +#define CIF_CSI_FRM1_ADDR_UV_ID0 0xb0 +#define CIF_CSI_FRM0_VLW_Y_ID0 0xb4 +#define CIF_CSI_FRM1_VLW_Y_ID0 0xb8 +#define CIF_CSI_FRM0_VLW_UV_ID0 0xbc +#define CIF_CSI_FRM1_VLW_UV_ID0 0xc0 +#define CIF_CSI_FRM0_ADDR_Y_ID1 0xc4 +#define CIF_CSI_FRM1_ADDR_Y_ID1 0xc8 +#define CIF_CSI_FRM0_ADDR_UV_ID1 0xcc +#define CIF_CSI_FRM1_ADDR_UV_ID1 0xd0 +#define CIF_CSI_FRM0_VLW_Y_ID1 0xd4 +#define CIF_CSI_FRM1_VLW_Y_ID1 0xd8 +#define CIF_CSI_FRM0_VLW_UV_ID1 0xdc +#define CIF_CSI_FRM1_VLW_UV_ID1 0xe0 +#define CIF_CSI_FRM0_ADDR_Y_ID2 0xe4 +#define CIF_CSI_FRM1_ADDR_Y_ID2 0xe8 +#define CIF_CSI_FRM0_ADDR_UV_ID2 0xec +#define CIF_CSI_FRM1_ADDR_UV_ID2 0xf0 +#define CIF_CSI_FRM0_VLW_Y_ID2 0xf4 +#define CIF_CSI_FRM1_VLW_Y_ID2 0xf8 +#define CIF_CSI_FRM0_VLW_UV_ID2 0xfc +#define CIF_CSI_FRM1_VLW_UV_ID2 0x100 +#define CIF_CSI_FRM0_ADDR_Y_ID3 0x104 +#define CIF_CSI_FRM1_ADDR_Y_ID3 0x108 +#define CIF_CSI_FRM0_ADDR_UV_ID3 0x10c +#define CIF_CSI_FRM1_ADDR_UV_ID3 0x110 +#define CIF_CSI_FRM0_VLW_Y_ID3 0x114 +#define CIF_CSI_FRM1_VLW_Y_ID3 0x118 +#define CIF_CSI_FRM0_VLW_UV_ID3 0x11c +#define CIF_CSI_FRM1_VLW_UV_ID3 0x120 +#define CIF_CSI_INTEN 0x124 +#define CIF_CSI_INTSTAT 0x128 +#define CIF_CSI_LINE_INT_NUM_ID0_1 0x12c +#define CIF_CSI_LINE_INT_NUM_ID2_3 0x130 +#define CIF_CSI_LINE_CNT_ID0_1 0x134 +#define CIF_CSI_LINE_CNT_ID2_3 0x138 +#define CIF_CSI_ID0_CROP_START 0x13c +#define CIF_CSI_ID1_CROP_START 0x140 +#define CIF_CSI_ID2_CROP_START 0x144 +#define CIF_CSI_ID3_CROP_START 0x148 + /* The key register bit description */ /* CIF_CTRL Reg */ @@ -119,4 +172,72 @@ #define CIF_CROP_Y_SHIFT 16 #define CIF_CROP_X_SHIFT 0 +/* CIF_CSI_ID_CTRL0 */ +#define CSI_DISABLE_CAPTURE (0x0 << 0) +#define CSI_ENABLE_CAPTURE (0x1 << 0) +#define CSI_WRDDR_TYPE_RAW8 (0x0 << 1) +#define CSI_WRDDR_TYPE_RAW10 (0x1 << 1) +#define CSI_WRDDR_TYPE_RAW12 (0x2 << 1) +#define CSI_WRDDR_TYPE_RGB888 (0x3 << 1) +#define CSI_WRDDR_TYPE_YUV422 (0x4 << 1) +#define CSI_DISABLE_COMMAND_MODE (0x0 << 4) +#define CSI_ENABLE_COMMAND_MODE (0x1 << 4) +#define CSI_DISABLE_CROP (0x0 << 5) +#define CSI_ENABLE_CROP (0x1 << 5) + +/* CIF_CSI_INTEN */ +#define CSI_FRAME0_START_INTEN(id) (0x1 << ((id) * 2)) +#define CSI_FRAME1_START_INTEN(id) (0x1 << ((id) * 2 + 1)) +#define CSI_FRAME0_END_INTEN(id) (0x1 << ((id) * 2 + 8)) +#define CSI_FRAME1_END_INTEN(id) (0x1 << ((id) * 2 + 9)) +#define CSI_DMA_Y_FIFO_OVERFLOW_INTEN (0x1 << 16) +#define CSI_DMA_UV_FIFO_OVERFLOW_INTEN (0x1 << 17) +#define CSI_CONFIG_FIFO_OVERFLOW_INTEN (0x1 << 18) +#define CSI_BANDWIDTH_LACK_INTEN (0x1 << 19) +#define CSI_RX_FIFO_OVERFLOW_INTEN (0x1 << 20) +#define CSI_ALL_FRAME_START_INTEN (0xff << 0) +#define CSI_ALL_FRAME_END_INTEN (0xff << 8) +#define CSI_ALL_ERROR_INTEN (0x1f << 16) + +/* CIF_CSI_INTSTAT */ +#define CSI_FRAME0_START_ID0 (0x1 << 0) +#define CSI_FRAME1_START_ID0 (0x1 << 1) +#define CSI_FRAME0_START_ID1 (0x1 << 2) +#define CSI_FRAME1_START_ID1 (0x1 << 3) +#define CSI_FRAME0_START_ID2 (0x1 << 4) +#define CSI_FRAME1_START_ID2 (0x1 << 5) +#define CSI_FRAME0_START_ID3 (0x1 << 6) +#define CSI_FRAME1_START_ID3 (0x1 << 7) +#define CSI_FRAME0_END_ID0 (0x1 << 8) +#define CSI_FRAME1_END_ID0 (0x1 << 9) +#define CSI_FRAME0_END_ID1 (0x1 << 10) +#define CSI_FRAME1_END_ID1 (0x1 << 11) +#define CSI_FRAME0_END_ID2 (0x1 << 12) +#define CSI_FRAME1_END_ID2 (0x1 << 13) +#define CSI_FRAME0_END_ID3 (0x1 << 14) +#define CSI_FRAME1_END_ID3 (0x1 << 15) +#define CSI_DMA_Y_FIFO_OVERFLOW (0x1 << 16) +#define CSI_DMA_UV_FIFO_OVERFLOW (0x1 << 17) +#define CSI_CONFIG_FIFO_OVERFLOW (0x1 << 18) +#define CSI_BANDWIDTH_LACK (0x1 << 19) +#define CSI_RX_FIFO_OVERFLOW (0x1 << 20) + +#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW | \ + CSI_DMA_UV_FIFO_OVERFLOW | \ + CSI_CONFIG_FIFO_OVERFLOW | \ + CSI_RX_FIFO_OVERFLOW) + +/* CSI Host Registers Define */ +#define CSIHOST_N_LANES 0x04 +#define CSIHOST_PHY_RSTZ 0x0c +#define CSIHOST_RESETN 0x10 +#define CSIHOST_CONTROL 0x40 + +#define SW_CPHY_EN(x) ((x) << 0) +#define SW_DSI_EN(x) ((x) << 4) +#define SW_DATATYPE_FS(x) ((x) << 8) +#define SW_DATATYPE_FE(x) ((x) << 14) +#define SW_DATATYPE_LS(x) ((x) << 20) +#define SW_DATATYPE_LE(x) ((x) << 26) + #endif