diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 7121ff6bc5f9..91a1581f779c 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -31,6 +31,10 @@ #define STREAM_PAD_SINK 0 #define STREAM_PAD_SOURCE 1 +#define CIF_FETCH_Y_LAST_LINE(VAL) ((VAL) & 0x1fff) +/* Check if swap y and c in bt1120 mode */ +#define CIF_FETCH_IS_Y_FIRST(VAL) ((VAL) & 0xf) + /* TODO: pingpong mode is not yet supported */ /* Get xsubs and ysubs for fourcc formats @@ -168,18 +172,42 @@ static const struct cif_input_fmt in_fmts[] = { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .fmt_val = CSI_WRDDR_TYPE_RAW8, @@ -296,7 +324,8 @@ cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect) rect->height = fmt.format.height; for (i = 0; i < ARRAY_SIZE(in_fmts); i++) - if (fmt.format.code == in_fmts[i].mbus_code) + if (fmt.format.code == in_fmts[i].mbus_code && + fmt.format.field == in_fmts[i].field) return &in_fmts[i]; v4l2_err(sd->v4l2_dev, "remote sensor mbus code not supported\n"); @@ -870,6 +899,9 @@ static u32 rkcif_determine_input_mode(struct rkcif_device *dev) case V4L2_STD_PAL: mode = INPUT_MODE_PAL; break; + case V4L2_STD_ATSC: + mode = INPUT_MODE_BT1120; + break; default: v4l2_err(&dev->v4l2_dev, "std: %lld is not supported", std); @@ -899,7 +931,8 @@ static inline u32 rkcif_scl_ctl(struct rkcif_stream *stream) static int rkcif_stream_start(struct rkcif_stream *stream) { - u32 val, mbus_flags, href_pol, vsync_pol, skip_top = 0; + u32 val, mbus_flags, href_pol, vsync_pol, + xfer_mode = 0, yc_swap = 0, skip_top = 0; struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *sensor_info; void __iomem *base = dev->base_addr; @@ -913,8 +946,18 @@ static int rkcif_stream_start(struct rkcif_stream *stream) vsync_pol = (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ? VSY_HIGH_ACTIVE : VSY_LOW_ACTIVE; + if (rkcif_determine_input_mode(dev) == INPUT_MODE_BT1120) { + if (stream->cif_fmt_in->field == V4L2_FIELD_NONE) + xfer_mode = BT1120_TRANSMIT_PROGRESS; + else + xfer_mode = BT1120_TRANSMIT_INTERFACE; + if (!CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->fmt_val)) + yc_swap = BT1120_YC_SWAP; + } + val = vsync_pol | href_pol | rkcif_determine_input_mode(dev) | - stream->cif_fmt_out->fmt_val | stream->cif_fmt_in->fmt_val; + stream->cif_fmt_out->fmt_val | stream->cif_fmt_in->fmt_val | + xfer_mode | yc_swap; write_cif_reg(base, CIF_FOR, val); write_cif_reg(base, CIF_VIR_LINE_WIDTH, stream->pixm.width); write_cif_reg(base, CIF_SET_SIZE, @@ -928,12 +971,21 @@ static int rkcif_stream_start(struct rkcif_stream *stream) write_cif_reg(base, CIF_INTSTAT, INTSTAT_CLS); write_cif_reg(base, CIF_SCL_CTRL, rkcif_scl_ctl(stream)); - /* Set up an buffer for the next frame */ - rkcif_assign_new_buffer_oneframe(stream); + if (dev->chip_id == CHIP_RK1808_CIF && + rkcif_determine_input_mode(dev) == INPUT_MODE_BT1120) + rkcif_assign_new_buffer_pingpong(stream, 1, 0); + else + /* Set up an buffer for the next frame */ + rkcif_assign_new_buffer_oneframe(stream); write_cif_reg(base, CIF_INTEN, FRAME_END_EN | PST_INF_FRAME_END); - write_cif_reg(base, CIF_CTRL, - AXI_BURST_16 | MODE_ONEFRAME | ENABLE_CAPTURE); + if (dev->chip_id == CHIP_RK1808_CIF && + rkcif_determine_input_mode(dev) == INPUT_MODE_BT1120) + write_cif_reg(base, CIF_CTRL, + AXI_BURST_16 | MODE_PINGPONG | ENABLE_CAPTURE); + else + write_cif_reg(base, CIF_CTRL, + AXI_BURST_16 | MODE_ONEFRAME | ENABLE_CAPTURE); stream->state = RKCIF_STATE_STREAMING; @@ -1503,6 +1555,68 @@ void rkcif_irq_pingpong(struct rkcif_device *cif_dev) stream->frame_idx++; } } else { - /* TODO */ + u32 lastline, lastpix, ctl, cif_frmst; + void __iomem *base = cif_dev->base_addr; + + intstat = read_cif_reg(base, CIF_INTSTAT); + cif_frmst = read_cif_reg(base, CIF_FRAME_STATUS); + lastline = CIF_FETCH_Y_LAST_LINE(read_cif_reg(base, CIF_LAST_LINE)); + lastpix = read_cif_reg(base, CIF_LAST_PIX); + ctl = read_cif_reg(base, CIF_CTRL); + + /* There are two irqs enabled: + * - PST_INF_FRAME_END: cif FIFO is ready, + * this is prior to FRAME_END + * - FRAME_END: cif has saved frame to memory, + * a frame ready + */ + + if ((intstat & PST_INF_FRAME_END)) { + write_cif_reg(base, CIF_INTSTAT, PST_INF_FRAME_END_CLR); + + if (stream->stopping) + /* To stop CIF ASAP, before FRAME_END irq */ + write_cif_reg(base, CIF_CTRL, + ctl & (~ENABLE_CAPTURE)); + } + + if ((intstat & FRAME_END)) { + struct vb2_v4l2_buffer *vb_done = NULL; + + write_cif_reg(base, CIF_INTSTAT, FRAME_END_CLR); + + if (stream->stopping) { + rkcif_stream_stop(stream); + stream->stopping = false; + wake_up(&stream->wq_stopped); + return; + } + + if (lastline != stream->pixm.height || + (!(cif_frmst & CIF_F0_READY) && + !(cif_frmst & CIF_F1_READY))) { + v4l2_err(&cif_dev->v4l2_dev, + "Bad frame, irq:0x%x frmst:0x%x size:%dx%d\n", + intstat, cif_frmst, lastline, lastpix); + return; + } + + if (cif_frmst & CIF_F0_READY) { + if (stream->curr_buf) + vb_done = &stream->curr_buf->vb; + stream->frame_phase = 0; + } else if (cif_frmst & CIF_F1_READY) { + if (stream->next_buf) + vb_done = &stream->next_buf->vb; + stream->frame_phase = 1; + } + + rkcif_assign_new_buffer_pingpong(stream, 0, 0); + + if (vb_done) + rkcif_vb_done_oneframe(stream, vb_done); + + stream->frame_idx++; + } } } diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index c61657adbcec..2f71855e3f59 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -108,11 +108,13 @@ enum cif_fmt_type { * * @mbus_code: mbus format * @fmt_val: the fmt val corresponding to CIF_FOR register + * @field: the field type of the input from sensor */ struct cif_input_fmt { u32 mbus_code; u32 fmt_val; enum cif_fmt_type fmt_type; + enum v4l2_field field; }; struct csi_channel_info { diff --git a/drivers/media/platform/rockchip/cif/regs.h b/drivers/media/platform/rockchip/cif/regs.h index fae14d16444d..958d07035c86 100644 --- a/drivers/media/platform/rockchip/cif/regs.h +++ b/drivers/media/platform/rockchip/cif/regs.h @@ -127,6 +127,7 @@ #define INPUT_MODE_YUV (0x00 << 2) #define INPUT_MODE_PAL (0x02 << 2) #define INPUT_MODE_NTSC (0x03 << 2) +#define INPUT_MODE_BT1120 (0x07 << 2) #define INPUT_MODE_RAW (0x04 << 2) #define INPUT_MODE_JPEG (0x05 << 2) #define INPUT_MODE_MIPI (0x06 << 2) @@ -151,6 +152,11 @@ #define RAWD_DATA_BIG_ENDIAN (0x01 << 18) #define UV_STORAGE_ORDER_UVUV (0x00 << 19) #define UV_STORAGE_ORDER_VUVU (0x01 << 19) +#define BT1120_CLOCK_SINGLE_EDGES (0x00 << 24) +#define BT1120_CLOCK_DOUBLE_EDGES (0x01 << 24) +#define BT1120_TRANSMIT_INTERFACE (0x00 << 25) +#define BT1120_TRANSMIT_PROGRESS (0x01 << 25) +#define BT1120_YC_SWAP (0x01 << 26) /* CIF_SCL_CTRL */ #define ENABLE_SCL_DOWN (0x01 << 0)