// SPDX-License-Identifier: GPL-2.0 /* * Rockchip CIF Driver * * Copyright (C) 2018 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dev.h" #include "mipi-csi2.h" #define CIF_REQ_BUFS_MIN 3 #define CIF_MIN_WIDTH 64 #define CIF_MIN_HEIGHT 64 #define CIF_MAX_WIDTH 8192 #define CIF_MAX_HEIGHT 8192 #define OUTPUT_STEP_WISE 8 #define RKCIF_PLANE_Y 0 #define RKCIF_PLANE_CBCR 1 #define STREAM_PAD_SINK 0 #define STREAM_PAD_SOURCE 1 #define CIF_TIMEOUT_FRAME_NUM (2) #define CIF_DVP_PCLK_DUAL_EDGE (V4L2_MBUS_PCLK_SAMPLE_RISING |\ V4L2_MBUS_PCLK_SAMPLE_FALLING) /* * Round up height when allocate memory so that Rockchip encoder can * use DMA buffer directly, though this may waste a bit of memory. */ #define MEMORY_ALIGN_ROUND_UP_HEIGHT 16 /* Get xsubs and ysubs for fourcc formats * * @xsubs: horizontal color samples in a 4*4 matrix, for yuv * @ysubs: vertical color samples in a 4*4 matrix, for yuv */ static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) { switch (fcc) { case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: *xsubs = 2; *ysubs = 1; break; case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV12: *xsubs = 2; *ysubs = 2; break; default: return -EINVAL; } return 0; } static const struct cif_output_fmt out_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV16, .cplanes = 2, .mplanes = 1, .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_NV61, .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_VUVU, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_NV12, .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_UVUV, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_YUV420SP, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_NV21, .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_VUVU, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_YUV420SP, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_YUYV, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_YVYU, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_UYVY, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_VYUY, .cplanes = 2, .mplanes = 1, .bpp = { 8, 16 }, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_YUV, }, { .fourcc = V4L2_PIX_FMT_RGB24, .cplanes = 1, .mplanes = 1, .bpp = { 24 }, .csi_fmt_val = CSI_WRDDR_TYPE_RGB888, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_RGB565, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_BGR666, .cplanes = 1, .mplanes = 1, .bpp = { 18 }, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB8, .cplanes = 1, .mplanes = 1, .bpp = { 8 }, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .cplanes = 1, .mplanes = 1, .bpp = { 8 }, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .cplanes = 1, .mplanes = 1, .bpp = { 8 }, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .cplanes = 1, .mplanes = 1, .bpp = { 8 }, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB10, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG10, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG10, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR10, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB12, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG12, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG12, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR12, .cplanes = 1, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR16, .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, .mplanes = 1, .bpp = { 16 }, .raw_bpp = 16, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4L2_PIX_FMT_GREY, .cplanes = 1, .mplanes = 1, .bpp = {8}, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4l2_PIX_FMT_EBD8, .cplanes = 1, .mplanes = 1, .bpp = {8}, .raw_bpp = 8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, }, { .fourcc = V4l2_PIX_FMT_SPD16, .cplanes = 1, .mplanes = 1, .bpp = {16}, .raw_bpp = 16, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, } /* TODO: We can support NV12M/NV21M/NV16M/NV61M too */ }; static const struct cif_input_fmt in_fmts[] = { { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_YUYV, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_YUYV, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_YVYU, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_YVYU, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_UYVY, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_UYVY, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_VYUY, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, .csi_yuv_order = CSI_YUV_INPUT_ORDER_VYUY, .fmt_type = CIF_FMT_TYPE_YUV, .field = V4L2_FIELD_INTERLACED, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, .csi_fmt_val = CSI_WRDDR_TYPE_RGB888, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_Y8_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_Y10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_Y12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_EBD_1X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, }, { .mbus_code = MEDIA_BUS_FMT_SPD_2X8, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, .fmt_type = CIF_FMT_TYPE_RAW, .field = V4L2_FIELD_NONE, } }; static struct v4l2_subdev *get_remote_sensor(struct rkcif_stream *stream, u16 *index) { struct media_pad *local, *remote; struct media_entity *sensor_me; struct v4l2_subdev *sub = NULL; local = &stream->vnode.vdev.entity.pads[0]; if (!local) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: video pad[0] is null\n", __func__); return NULL; } remote = media_entity_remote_pad(local); if (!remote) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: remote pad is null\n", __func__); return NULL; } if (index) *index = remote->index; sensor_me = remote->entity; sub = media_entity_to_v4l2_subdev(sensor_me); return sub; } static void get_remote_terminal_sensor(struct rkcif_stream *stream, struct v4l2_subdev **sensor_sd) { struct media_graph graph; struct media_entity *entity = &stream->vnode.vdev.entity; struct media_device *mdev = entity->graph_obj.mdev; int ret; /* Walk the graph to locate sensor nodes. */ mutex_lock(&mdev->graph_mutex); ret = media_graph_walk_init(&graph, mdev); if (ret) { mutex_unlock(&mdev->graph_mutex); *sensor_sd = NULL; return; } media_graph_walk_start(&graph, entity); while ((entity = media_graph_walk_next(&graph))) { if (entity->function == MEDIA_ENT_F_CAM_SENSOR) break; } mutex_unlock(&mdev->graph_mutex); media_graph_walk_cleanup(&graph); if (entity) *sensor_sd = media_entity_to_v4l2_subdev(entity); else *sensor_sd = NULL; } static struct rkcif_sensor_info *sd_to_sensor(struct rkcif_device *dev, struct v4l2_subdev *sd) { u32 i; for (i = 0; i < dev->num_sensors; ++i) if (dev->sensors[i].sd == sd) return &dev->sensors[i]; if (i == dev->num_sensors) { for (i = 0; i < dev->num_sensors; ++i) { if (dev->sensors[i].mbus.type == V4L2_MBUS_CCP2) return &dev->lvds_subdev.sensor_self; } } return NULL; } static unsigned char get_data_type(u32 pixelformat, u8 cmd_mode_en, u8 dsi_input) { switch (pixelformat) { /* csi raw8 */ case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_Y8_1X8: return 0x2a; /* csi raw10 */ case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_Y10_1X10: return 0x2b; /* csi raw12 */ case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SRGGB12_1X12: case MEDIA_BUS_FMT_Y12_1X12: return 0x2c; /* csi uyvy 422 */ case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: return 0x1e; case MEDIA_BUS_FMT_RGB888_1X24: { if (dsi_input) { if (cmd_mode_en) /* dsi command mode*/ return 0x39; else /* dsi video mode */ return 0x3e; } else { return 0x24; } } case MEDIA_BUS_FMT_EBD_1X8: return 0x12; case MEDIA_BUS_FMT_SPD_2X8: return 0x2f; default: return 0x2b; } } static int get_csi_crop_align(const struct cif_input_fmt *fmt_in) { switch (fmt_in->csi_fmt_val) { case CSI_WRDDR_TYPE_RGB888: return 24; case CSI_WRDDR_TYPE_RAW10: case CSI_WRDDR_TYPE_RAW12: return 4; case CSI_WRDDR_TYPE_RAW8: case CSI_WRDDR_TYPE_YUV422: return 8; default: return -1; } } static const struct cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect, u32 pad, int *vc) { struct v4l2_subdev_format fmt; int ret; u32 i; struct rkmodule_vc_fmt_info vc_info = {0}; ret = v4l2_subdev_call(sd, core, ioctl, RKMODULE_GET_VC_FMT_INFO, &vc_info); if (ret < 0) v4l2_warn(sd->v4l2_dev, "get sensor vc info failed, maybe not support\n"); fmt.pad = pad; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt.reserved[0] = 0; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (ret < 0) { v4l2_warn(sd->v4l2_dev, "sensor fmt invalid, set to default size\n"); goto set_default; } /* v4l2_subdev_format reserved[0] * using as mipi virtual channel */ switch (fmt.reserved[0]) { case V4L2_MBUS_CSI2_CHANNEL_3: *vc = 3; break; case V4L2_MBUS_CSI2_CHANNEL_2: *vc = 2; break; case V4L2_MBUS_CSI2_CHANNEL_1: *vc = 1; break; case V4L2_MBUS_CSI2_CHANNEL_0: *vc = 0; break; default: *vc = -1; } v4l2_dbg(1, rkcif_debug, sd->v4l2_dev, "remote fmt: mbus code:0x%x, size:%dx%d, field: %d\n", fmt.format.code, fmt.format.width, fmt.format.height, fmt.format.field); rect->left = 0; rect->top = 0; rect->width = fmt.format.width; rect->height = fmt.format.height; for (i = 0; i < ARRAY_SIZE(in_fmts); i++) 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"); set_default: rect->left = 0; rect->top = 0; rect->width = RKCIF_DEFAULT_WIDTH; rect->height = RKCIF_DEFAULT_HEIGHT; return NULL; } static const struct cif_output_fmt *find_output_fmt(struct rkcif_stream *stream, u32 pixelfmt) { const struct cif_output_fmt *fmt; u32 i; for (i = 0; i < ARRAY_SIZE(out_fmts); i++) { fmt = &out_fmts[i]; if (fmt->fourcc == pixelfmt) return fmt; } return NULL; } static enum cif_reg_index get_reg_index_of_id_ctrl0(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_ID0_CTRL0; break; case 1: index = CIF_REG_MIPI_LVDS_ID1_CTRL0; break; case 2: index = CIF_REG_MIPI_LVDS_ID2_CTRL0; break; case 3: index = CIF_REG_MIPI_LVDS_ID3_CTRL0; break; default: index = CIF_REG_MIPI_LVDS_ID0_CTRL0; break; } return index; } static enum cif_reg_index get_reg_index_of_id_ctrl1(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_ID0_CTRL1; break; case 1: index = CIF_REG_MIPI_LVDS_ID1_CTRL1; break; case 2: index = CIF_REG_MIPI_LVDS_ID2_CTRL1; break; case 3: index = CIF_REG_MIPI_LVDS_ID3_CTRL1; break; default: index = CIF_REG_MIPI_LVDS_ID0_CTRL1; break; } return index; } static enum cif_reg_index get_reg_index_of_frm0_y_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm1_y_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm0_uv_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm1_uv_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm0_y_vlw(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm1_y_vlw(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm0_uv_vlw(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_frm1_uv_vlw(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0; break; case 1: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID1; break; case 2: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID2; break; case 3: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID3; break; default: index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_id_crop_start(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_MIPI_LVDS_ID0_CROP_START; break; case 1: index = CIF_REG_MIPI_LVDS_ID1_CROP_START; break; case 2: index = CIF_REG_MIPI_LVDS_ID2_CROP_START; break; case 3: index = CIF_REG_MIPI_LVDS_ID3_CROP_START; break; default: index = CIF_REG_MIPI_LVDS_ID0_CROP_START; break; } return index; } static enum cif_reg_index get_reg_index_of_lvds_sav_eav_act0(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_LVDS_SAV_EAV_ACT0_ID0; break; case 1: index = CIF_REG_LVDS_SAV_EAV_ACT0_ID1; break; case 2: index = CIF_REG_LVDS_SAV_EAV_ACT0_ID2; break; case 3: index = CIF_REG_LVDS_SAV_EAV_ACT0_ID3; break; default: index = CIF_REG_LVDS_SAV_EAV_ACT0_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_lvds_sav_eav_act1(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_LVDS_SAV_EAV_ACT1_ID0; break; case 1: index = CIF_REG_LVDS_SAV_EAV_ACT1_ID1; break; case 2: index = CIF_REG_LVDS_SAV_EAV_ACT1_ID2; break; case 3: index = CIF_REG_LVDS_SAV_EAV_ACT1_ID3; break; default: index = CIF_REG_LVDS_SAV_EAV_ACT1_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_lvds_sav_eav_blk0(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_LVDS_SAV_EAV_BLK0_ID0; break; case 1: index = CIF_REG_LVDS_SAV_EAV_BLK0_ID1; break; case 2: index = CIF_REG_LVDS_SAV_EAV_BLK0_ID2; break; case 3: index = CIF_REG_LVDS_SAV_EAV_BLK0_ID3; break; default: index = CIF_REG_LVDS_SAV_EAV_BLK0_ID0; break; } return index; } static enum cif_reg_index get_reg_index_of_lvds_sav_eav_blk1(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_LVDS_SAV_EAV_BLK1_ID0; break; case 1: index = CIF_REG_LVDS_SAV_EAV_BLK1_ID1; break; case 2: index = CIF_REG_LVDS_SAV_EAV_BLK1_ID2; break; case 3: index = CIF_REG_LVDS_SAV_EAV_BLK1_ID3; break; default: index = CIF_REG_LVDS_SAV_EAV_BLK1_ID0; break; } return index; } static enum cif_reg_index get_dvp_reg_index_of_frm0_y_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_DVP_FRM0_ADDR_Y; break; case 1: index = CIF_REG_DVP_FRM0_ADDR_Y_ID1; break; case 2: index = CIF_REG_DVP_FRM0_ADDR_Y_ID2; break; case 3: index = CIF_REG_DVP_FRM0_ADDR_Y_ID3; break; default: index = CIF_REG_DVP_FRM0_ADDR_Y; break; } return index; } static enum cif_reg_index get_dvp_reg_index_of_frm1_y_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_DVP_FRM1_ADDR_Y; break; case 1: index = CIF_REG_DVP_FRM1_ADDR_Y_ID1; break; case 2: index = CIF_REG_DVP_FRM1_ADDR_Y_ID2; break; case 3: index = CIF_REG_DVP_FRM1_ADDR_Y_ID3; break; default: index = CIF_REG_DVP_FRM0_ADDR_Y; break; } return index; } static enum cif_reg_index get_dvp_reg_index_of_frm0_uv_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_DVP_FRM0_ADDR_UV; break; case 1: index = CIF_REG_DVP_FRM0_ADDR_UV_ID1; break; case 2: index = CIF_REG_DVP_FRM0_ADDR_UV_ID2; break; case 3: index = CIF_REG_DVP_FRM0_ADDR_UV_ID3; break; default: index = CIF_REG_DVP_FRM0_ADDR_UV; break; } return index; } static enum cif_reg_index get_dvp_reg_index_of_frm1_uv_addr(int channel_id) { enum cif_reg_index index; switch (channel_id) { case 0: index = CIF_REG_DVP_FRM1_ADDR_UV; break; case 1: index = CIF_REG_DVP_FRM1_ADDR_UV_ID1; break; case 2: index = CIF_REG_DVP_FRM1_ADDR_UV_ID2; break; case 3: index = CIF_REG_DVP_FRM1_ADDR_UV_ID3; break; default: index = CIF_REG_DVP_FRM1_ADDR_UV; break; } return index; } /***************************** stream operations ******************************/ static int rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream, enum rkcif_yuvaddr_state stat) { struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; struct rkcif_buffer *buffer = NULL; u32 frm_addr_y = CIF_REG_DVP_FRM0_ADDR_Y; u32 frm_addr_uv = CIF_REG_DVP_FRM0_ADDR_UV; unsigned long flags; int ret = 0; spin_lock_irqsave(&stream->vbq_lock, flags); if (stat == RKCIF_YUV_ADDR_STATE_INIT) { if (!stream->curr_buf) { if (!list_empty(&stream->buf_head)) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); list_del(&stream->curr_buf->queue); } } if (stream->curr_buf) { rkcif_write_register(dev, CIF_REG_DVP_FRM0_ADDR_Y, stream->curr_buf->buff_addr[RKCIF_PLANE_Y]); rkcif_write_register(dev, CIF_REG_DVP_FRM0_ADDR_UV, stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, CIF_REG_DVP_FRM0_ADDR_Y, dummy_buf->dma_addr); rkcif_write_register(dev, CIF_REG_DVP_FRM0_ADDR_UV, dummy_buf->dma_addr); } } if (!stream->next_buf) { if (!list_empty(&stream->buf_head)) { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); list_del(&stream->next_buf->queue); } } if (stream->next_buf) { rkcif_write_register(dev, CIF_REG_DVP_FRM1_ADDR_Y, stream->next_buf->buff_addr[RKCIF_PLANE_Y]); rkcif_write_register(dev, CIF_REG_DVP_FRM1_ADDR_UV, stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, CIF_REG_DVP_FRM1_ADDR_Y, dummy_buf->dma_addr); rkcif_write_register(dev, CIF_REG_DVP_FRM1_ADDR_UV, dummy_buf->dma_addr); } } } else if (stat == RKCIF_YUV_ADDR_STATE_UPDATE) { if (!list_empty(&stream->buf_head)) { if (stream->frame_phase == CIF_CSI_FRAME0_READY) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); list_del(&stream->curr_buf->queue); buffer = stream->curr_buf; } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { 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 (dummy_buf->vaddr && stream->frame_phase == CIF_CSI_FRAME0_READY) stream->curr_buf = NULL; if (dummy_buf->vaddr && stream->frame_phase == CIF_CSI_FRAME1_READY) stream->next_buf = NULL; buffer = NULL; } if (stream->frame_phase == CIF_CSI_FRAME0_READY) { frm_addr_y = CIF_REG_DVP_FRM0_ADDR_Y; frm_addr_uv = CIF_REG_DVP_FRM0_ADDR_UV; } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { frm_addr_y = CIF_REG_DVP_FRM1_ADDR_Y; frm_addr_uv = CIF_REG_DVP_FRM1_ADDR_UV; } if (buffer) { rkcif_write_register(dev, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]); rkcif_write_register(dev, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr); rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr); } else { ret = -EINVAL; } v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "not active buffer, frame Drop\n"); } } spin_unlock_irqrestore(&stream->vbq_lock, flags); return ret; } static void rkcif_assign_new_buffer_init(struct rkcif_stream *stream, int channel_id) { struct rkcif_device *dev = stream->cifdev; struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; u32 frm0_addr_y, frm0_addr_uv; u32 frm1_addr_y, frm1_addr_uv; unsigned long flags; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; struct csi_channel_info *channel = &dev->channels[channel_id]; if (mbus_cfg->type == V4L2_MBUS_CSI2 || 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); frm1_addr_y = get_reg_index_of_frm1_y_addr(channel_id); frm1_addr_uv = get_reg_index_of_frm1_uv_addr(channel_id); } else { frm0_addr_y = get_dvp_reg_index_of_frm0_y_addr(channel_id); frm0_addr_uv = get_dvp_reg_index_of_frm0_uv_addr(channel_id); frm1_addr_y = get_dvp_reg_index_of_frm1_y_addr(channel_id); frm1_addr_uv = get_dvp_reg_index_of_frm1_uv_addr(channel_id); } spin_lock_irqsave(&stream->vbq_lock, flags); if (!stream->curr_buf) { if (!list_empty(&stream->buf_head)) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); list_del(&stream->curr_buf->queue); } } if (stream->curr_buf) { rkcif_write_register(dev, frm0_addr_y, stream->curr_buf->buff_addr[RKCIF_PLANE_Y]); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm0_addr_uv, stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, frm0_addr_y, dummy_buf->dma_addr); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm0_addr_uv, dummy_buf->dma_addr); } } if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { stream->next_buf = stream->curr_buf; if (stream->next_buf) { rkcif_write_register(dev, frm1_addr_y, stream->next_buf->buff_addr[RKCIF_PLANE_Y] + (channel->virtual_width / 2)); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm1_addr_uv, stream->next_buf->buff_addr[RKCIF_PLANE_CBCR] + (channel->virtual_width / 2)); } } else { if (!stream->next_buf) { if (!list_empty(&stream->buf_head)) { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); list_del(&stream->next_buf->queue); } } if (stream->next_buf) { rkcif_write_register(dev, frm1_addr_y, stream->next_buf->buff_addr[RKCIF_PLANE_Y]); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm1_addr_uv, stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, frm1_addr_y, dummy_buf->dma_addr); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm1_addr_uv, dummy_buf->dma_addr); } } } spin_unlock_irqrestore(&stream->vbq_lock, flags); stream->is_dvp_yuv_addr_init = true; /* for BT.656/BT.1120 multi channels function, * yuv addr of unused channel must be set */ if (mbus_cfg->type == V4L2_MBUS_BT656) { int ch_id; for (ch_id = 0; ch_id < RKCIF_MAX_STREAM_DVP; ch_id++) { if (dev->stream[ch_id].is_dvp_yuv_addr_init) continue; if (dummy_buf->dma_addr) { rkcif_write_register(dev, get_dvp_reg_index_of_frm0_y_addr(ch_id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm0_uv_addr(ch_id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm1_y_addr(ch_id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm1_uv_addr(ch_id), dummy_buf->dma_addr); } } } } static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream, int channel_id) { struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; struct rkcif_buffer *buffer = NULL; u32 frm_addr_y, frm_addr_uv; struct csi_channel_info *channel = &dev->channels[channel_id]; int ret = 0; unsigned long flags; if (mbus_cfg->type == V4L2_MBUS_CSI2 || 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); frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_uv_addr(channel_id) : get_reg_index_of_frm1_uv_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); frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_dvp_reg_index_of_frm0_uv_addr(channel_id) : get_dvp_reg_index_of_frm1_uv_addr(channel_id); } spin_lock_irqsave(&stream->vbq_lock, flags); if (!list_empty(&stream->buf_head)) { if (!dummy_buf->vaddr && stream->curr_buf == stream->next_buf && stream->cif_fmt_in->field != V4L2_FIELD_INTERLACED) ret = -EINVAL; if (stream->frame_phase == CIF_CSI_FRAME0_READY) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->curr_buf) { list_del(&stream->curr_buf->queue); buffer = stream->curr_buf; } } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { if (stream->next_buf != stream->curr_buf) { stream->next_buf = stream->curr_buf; buffer = stream->next_buf; } else { buffer = NULL; } } else { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->next_buf) { list_del(&stream->next_buf->queue); buffer = stream->next_buf; } } } } else { buffer = NULL; if (dummy_buf->vaddr) { if (stream->frame_phase == CIF_CSI_FRAME0_READY) { stream->curr_buf = NULL; } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { stream->next_buf = stream->curr_buf; buffer = stream->next_buf; } else { stream->next_buf = NULL; } } } else if (stream->curr_buf != stream->next_buf) { if (stream->frame_phase == CIF_CSI_FRAME0_READY) { stream->curr_buf = stream->next_buf; buffer = stream->next_buf; } else if (stream->frame_phase == CIF_CSI_FRAME1_READY) { stream->next_buf = stream->curr_buf; buffer = stream->curr_buf; } } } stream->frame_phase_cache = stream->frame_phase; spin_unlock_irqrestore(&stream->vbq_lock, flags); if (buffer) { if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED && stream->frame_phase == CIF_CSI_FRAME1_READY) { rkcif_write_register(dev, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y] + (channel->virtual_width / 2)); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_CBCR] + (channel->virtual_width / 2)); } else { rkcif_write_register(dev, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_CBCR]); } } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr); } else { ret = -EINVAL; } v4l2_info(&dev->v4l2_dev, "not active buffer, skip current frame, %s stream[%d]\n", (mbus_cfg->type == V4L2_MBUS_CSI2 || mbus_cfg->type == V4L2_MBUS_CCP2) ? "mipi/lvds" : "dvp", stream->id); } return ret; } static int rkcif_get_new_buffer_wake_up_mode(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; int ret = 0; unsigned long flags; spin_lock_irqsave(&stream->vbq_lock, flags); if (!list_empty(&stream->buf_head)) { if (!dummy_buf->vaddr && stream->curr_buf == stream->next_buf) ret = -EINVAL; if (stream->line_int_cnt % 2) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->curr_buf) list_del(&stream->curr_buf->queue); } else { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->next_buf) list_del(&stream->next_buf->queue); } stream->is_buf_active = true; } else { stream->is_buf_active = false; if (dummy_buf->vaddr) { if (stream->line_int_cnt % 2) stream->curr_buf = NULL; else stream->next_buf = NULL; } else if (stream->curr_buf != stream->next_buf) { if (stream->line_int_cnt % 2) { stream->curr_buf = stream->next_buf; stream->frame_phase_cache = CIF_CSI_FRAME0_READY; } else { stream->next_buf = stream->curr_buf; stream->frame_phase_cache = CIF_CSI_FRAME1_READY; } stream->is_buf_active = true; } else { ret = -EINVAL; } } spin_unlock_irqrestore(&stream->vbq_lock, flags); return ret; } static int rkcif_update_new_buffer_wake_up_mode(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; struct rkcif_buffer *buffer = NULL; u32 frm_addr_y, frm_addr_uv; int channel_id = stream->id; int ret = 0; unsigned long flags; if (mbus_cfg->type == V4L2_MBUS_CSI2 || 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); frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_uv_addr(channel_id) : get_reg_index_of_frm1_uv_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); frm_addr_uv = stream->frame_phase & CIF_CSI_FRAME0_READY ? get_dvp_reg_index_of_frm0_uv_addr(channel_id) : get_dvp_reg_index_of_frm1_uv_addr(channel_id); } spin_lock_irqsave(&stream->vbq_lock, flags); if (stream->is_buf_active) { if (stream->frame_phase == CIF_CSI_FRAME0_READY) buffer = stream->curr_buf; else if (stream->frame_phase == CIF_CSI_FRAME1_READY) buffer = stream->next_buf; } spin_unlock_irqrestore(&stream->vbq_lock, flags); if (buffer) { rkcif_write_register(dev, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_CBCR]); } else { if (dummy_buf->vaddr) { rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr); } else { ret = -EINVAL; } v4l2_info(&dev->v4l2_dev, "not active buffer, skip current frame, %s stream[%d]\n", (mbus_cfg->type == V4L2_MBUS_CSI2 || mbus_cfg->type == V4L2_MBUS_CCP2) ? "mipi/lvds" : "dvp", stream->id); } return ret; } static void rkcif_assign_dummy_buffer(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; unsigned long flags; spin_lock_irqsave(&stream->vbq_lock, flags); /* for BT.656/BT.1120 multi channels function, * yuv addr of unused channel must be set */ if (mbus_cfg->type == V4L2_MBUS_BT656 && dummy_buf->vaddr) { rkcif_write_register(dev, get_dvp_reg_index_of_frm0_y_addr(stream->id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm0_uv_addr(stream->id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm1_y_addr(stream->id), dummy_buf->dma_addr); rkcif_write_register(dev, get_dvp_reg_index_of_frm1_uv_addr(stream->id), dummy_buf->dma_addr); } spin_unlock_irqrestore(&stream->vbq_lock, flags); } static int rkcif_assign_new_buffer_pingpong(struct rkcif_stream *stream, int init, int channel_id) { int ret = 0; if (init) rkcif_assign_new_buffer_init(stream, channel_id); else ret = rkcif_assign_new_buffer_update(stream, channel_id); return ret; } static void rkcif_csi_get_vc_num(struct rkcif_device *dev, unsigned int mbus_flags) { int i, vc_num = 0; for (i = 0; i < RKCIF_MAX_CSI_CHANNEL; i++) { if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_0) { dev->channels[vc_num].vc = vc_num; vc_num++; mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_0; continue; } if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_1) { dev->channels[vc_num].vc = vc_num; vc_num++; mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_1; continue; } if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_2) { dev->channels[vc_num].vc = vc_num; vc_num++; mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_2; continue; } if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_3) { dev->channels[vc_num].vc = vc_num; vc_num++; mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_3; continue; } } dev->num_channels = vc_num ? vc_num : 1; if (dev->num_channels == 1) dev->channels[0].vc = 0; } static void rkcif_csi_set_lvds_sav_eav(struct rkcif_stream *stream, struct csi_channel_info *channel) { struct rkcif_device *dev = stream->cifdev; struct rkmodule_lvds_cfg *lvds_cfg = &channel->lvds_cfg; struct rkmodule_lvds_frame_sync_code *frm_sync_code = NULL; 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) { 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; } else { 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 (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) { 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) 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]; } odd_sync_code = &frm_sync_code->odd_sync_code; even_sync_code = &frm_sync_code->even_sync_code; } if (odd_sync_code && even_sync_code) { rkcif_write_register(stream->cifdev, get_reg_index_of_lvds_sav_eav_act0(channel->id), SW_LVDS_EAV_ACT(odd_sync_code->act.eav) | SW_LVDS_SAV_ACT(odd_sync_code->act.sav)); rkcif_write_register(stream->cifdev, get_reg_index_of_lvds_sav_eav_blk0(channel->id), SW_LVDS_EAV_BLK(odd_sync_code->blk.eav) | SW_LVDS_SAV_BLK(odd_sync_code->blk.sav)); rkcif_write_register(stream->cifdev, get_reg_index_of_lvds_sav_eav_act1(channel->id), SW_LVDS_EAV_ACT(even_sync_code->act.eav) | SW_LVDS_SAV_ACT(even_sync_code->act.sav)); rkcif_write_register(stream->cifdev, get_reg_index_of_lvds_sav_eav_blk1(channel->id), SW_LVDS_EAV_BLK(even_sync_code->blk.eav) | SW_LVDS_SAV_BLK(even_sync_code->blk.sav)); } } static int rkcif_csi_channel_init(struct rkcif_stream *stream, struct csi_channel_info *channel) { struct rkcif_device *dev = stream->cifdev; const struct cif_output_fmt *fmt; u32 fourcc; channel->enable = 1; channel->width = stream->pixm.width; channel->height = stream->pixm.height; channel->fmt_val = stream->cif_fmt_out->csi_fmt_val; channel->cmd_mode_en = 0; /* default use DSI Video Mode */ channel->dsi_input = dev->terminal_sensor.dsi_input_en; if (stream->crop_enable) { channel->crop_en = 1; if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888) channel->crop_st_x = 3 * stream->crop[CROP_SRC_ACT].left; else channel->crop_st_x = stream->crop[CROP_SRC_ACT].left; channel->crop_st_y = stream->crop[CROP_SRC_ACT].top; channel->width = stream->crop[CROP_SRC_ACT].width; channel->height = stream->crop[CROP_SRC_ACT].height; } else { channel->width = stream->pixm.width; channel->height = stream->pixm.height; channel->crop_en = 0; } 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; } /* * for mipi or lvds, when enable compact, the virtual width of raw10/raw12 * needs aligned with :ALIGN(bits_per_pixel * width / 8, 8), if enable 16bit mode * needs aligned with :ALIGN(bits_per_pixel * width * 2, 8), to optimize reading and * writing of ddr, aliged with 256 */ if (fmt->fmt_type == CIF_FMT_TYPE_RAW && stream->is_compact && fmt->csi_fmt_val != CSI_WRDDR_TYPE_RGB888) { channel->virtual_width = ALIGN(channel->width * fmt->raw_bpp / 8, 256); } else { channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8); } if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888) channel->width = channel->width * fmt->bpp[0] / 8; /* * rk cif don't support output yuyv fmt data * if user request yuyv fmt, the input mode must be RAW8 * and the width is double Because the real input fmt is * yuyv */ fourcc = stream->cif_fmt_out->fourcc; if (fourcc == V4L2_PIX_FMT_YUYV || fourcc == V4L2_PIX_FMT_YVYU || fourcc == V4L2_PIX_FMT_UYVY || fourcc == V4L2_PIX_FMT_VYUY) { channel->fmt_val = CSI_WRDDR_TYPE_RAW8; channel->width *= 2; channel->virtual_width *= 2; } if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { channel->virtual_width *= 2; channel->height /= 2; } channel->data_type = get_data_type(stream->cif_fmt_in->mbus_code, channel->cmd_mode_en, channel->dsi_input); 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)) channel->vc = stream->vc; else channel->vc = channel->id; v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "%s: channel width %d, height %d, virtual_width %d, vc %d\n", __func__, channel->width, channel->height, channel->virtual_width, channel->vc); return 0; } static int rkcif_csi_channel_set(struct rkcif_stream *stream, struct csi_channel_info *channel, enum v4l2_mbus_type mbus_type) { unsigned int val = 0x0; struct rkcif_device *dev = stream->cifdev; struct rkcif_stream *detect_stream = &dev->stream[0]; unsigned int wait_line = 0x3fff; 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(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 (detect_stream->is_line_wake_up) { rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, CSI_LINE_INTEN(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)); rkcif_write_register(dev, CIF_REG_MIPI_WATER_LINE, CIF_MIPI_LVDS_SW_WATER_LINE_25_RK1808 | CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE_RK1808 | CIF_MIPI_LVDS_SW_HURRY_VALUE_RK1808(0x3) | CIF_MIPI_LVDS_SW_HURRY_ENABLE_RK1808); val = CIF_MIPI_LVDS_SW_PRESS_VALUE(0x7) | CIF_MIPI_LVDS_SW_PRESS_ENABLE | CIF_MIPI_LVDS_SW_HURRY_VALUE(0x7) | CIF_MIPI_LVDS_SW_HURRY_ENABLE | CIF_MIPI_LVDS_SW_WATER_LINE_25 | CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE; if (mbus_type == V4L2_MBUS_CSI2) { val &= ~CIF_MIPI_LVDS_SW_SEL_LVDS; } else if (mbus_type == V4L2_MBUS_CCP2) { if (channel->fmt_val == CSI_WRDDR_TYPE_RAW12) val |= CIF_MIPI_LVDS_SW_LVDS_WIDTH_12BITS; else if (channel->fmt_val == CSI_WRDDR_TYPE_RAW10) val |= CIF_MIPI_LVDS_SW_LVDS_WIDTH_10BITS; else val |= CIF_MIPI_LVDS_SW_LVDS_WIDTH_8BITS; val |= CIF_MIPI_LVDS_SW_SEL_LVDS; } rkcif_write_register(dev, CIF_REG_MIPI_LVDS_CTRL, val); rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN, CSI_ALL_ERROR_INTEN); 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); rkcif_write_register(dev, get_reg_index_of_frm1_y_vlw(channel->id), channel->virtual_width); rkcif_write_register(dev, get_reg_index_of_frm0_uv_vlw(channel->id), channel->virtual_width); rkcif_write_register(dev, get_reg_index_of_frm1_uv_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); /* Set up an buffer for the next frame */ rkcif_assign_new_buffer_pingpong(stream, RKCIF_YUV_ADDR_STATE_INIT, channel->id); if (mbus_type == V4L2_MBUS_CSI2) { //need always enable crop val = CSI_ENABLE_CAPTURE | channel->fmt_val | channel->cmd_mode_en << 4 | CSI_ENABLE_CROP | channel->vc << 8 | channel->data_type << 10; if (stream->is_compact) val |= CSI_ENABLE_MIPI_COMPACT; else val &= ~CSI_ENABLE_MIPI_COMPACT; if (stream->cifdev->chip_id >= CHIP_RK3568_CIF) val |= stream->cif_fmt_in->csi_yuv_order; } else if (mbus_type == V4L2_MBUS_CCP2) { 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; } if (stream->is_high_align) val |= CSI_ENABLE_MIPI_HIGH_ALIGN; else val &= ~CSI_ENABLE_MIPI_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) { struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *active_sensor = dev->active_sensor; unsigned int flags = active_sensor->mbus.flags; enum v4l2_mbus_type mbus_type = active_sensor->mbus.type; struct csi_channel_info *channel; u32 ret = 0; if (stream->state != RKCIF_STATE_RESET_IN_STREAMING) stream->frame_idx = 0; if (mbus_type == V4L2_MBUS_CSI2) { 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); } else if (mbus_type == V4L2_MBUS_CCP2) { rkcif_csi_get_vc_num(dev, flags); channel = &dev->channels[stream->id]; channel->id = stream->id; ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_LVDS_CFG, &channel->lvds_cfg); if (ret) { v4l2_err(&dev->v4l2_dev, "Err: get lvds config failed!!\n"); return ret; } 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; return 0; } static void rkcif_stream_stop(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; struct v4l2_mbus_config *mbus_cfg = &cif_dev->active_sensor->mbus; u32 val; int id; if (mbus_cfg->type == V4L2_MBUS_CSI2 || 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) val &= ~CSI_ENABLE_CAPTURE; else val &= ~LVDS_ENABLE_CAPTURE; rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(id), val); rkcif_write_register_or(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT, CSI_START_INTSTAT(id) | CSI_DMA_END_INTSTAT(id) | CSI_LINE_INTSTAT(id)); rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, ~(CSI_START_INTEN(id) | CSI_DMA_END_INTEN(id) | CSI_LINE_INTEN(id))); rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, ~CSI_ALL_ERROR_INTEN); } 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)); rkcif_write_register(cif_dev, CIF_REG_DVP_INTEN, 0x0); rkcif_write_register(cif_dev, CIF_REG_DVP_INTSTAT, 0x3ff); rkcif_write_register(cif_dev, CIF_REG_DVP_FRAME_STATUS, 0x0); } stream->is_dvp_yuv_addr_init = false; } stream->state = RKCIF_STATE_READY; } static bool rkcif_is_extending_line_for_height(struct rkcif_device *dev, struct rkcif_stream *stream, const struct cif_input_fmt *fmt) { bool is_extended = false; struct rkmodule_hdr_cfg hdr_cfg; int ret; if (dev->chip_id == CHIP_RV1126_CIF || dev->chip_id == CHIP_RV1126_CIF_LITE) { if (dev->terminal_sensor.sd) { ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) dev->hdr.mode = hdr_cfg.hdr_mode; else dev->hdr.mode = NO_HDR; } if (fmt && fmt->fmt_type == CIF_FMT_TYPE_RAW) { if ((dev->hdr.mode == HDR_X2 && stream->id == RKCIF_STREAM_MIPI_ID1) || (dev->hdr.mode == HDR_X3 && stream->id == RKCIF_STREAM_MIPI_ID2) || (dev->hdr.mode == NO_HDR)) { is_extended = true; } } } return is_extended; } static int rkcif_queue_setup(struct vb2_queue *queue, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], struct device *alloc_ctxs[]) { struct rkcif_stream *stream = queue->drv_priv; struct rkcif_extend_info *extend_line = &stream->extend_line; struct rkcif_device *dev = stream->cifdev; const struct v4l2_pix_format_mplane *pixm = NULL; const struct cif_output_fmt *cif_fmt; const struct cif_input_fmt *in_fmt; bool is_extended = false; u32 i, height; pixm = &stream->pixm; cif_fmt = stream->cif_fmt_out; in_fmt = stream->cif_fmt_in; *num_planes = cif_fmt->mplanes; if (stream->crop_enable) height = stream->crop[CROP_SRC_ACT].height; else height = pixm->height; is_extended = rkcif_is_extending_line_for_height(dev, stream, in_fmt); if (is_extended && extend_line->is_extended) { height = extend_line->pixm.height; pixm = &extend_line->pixm; } for (i = 0; i < cif_fmt->mplanes; i++) { const struct v4l2_plane_pix_format *plane_fmt; int h = round_up(height, MEMORY_ALIGN_ROUND_UP_HEIGHT); plane_fmt = &pixm->plane_fmt[i]; sizes[i] = plane_fmt->sizeimage / height * h; } v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s count %d, size %d, extended(%d, %d)\n", v4l2_type_names[queue->type], *num_buffers, sizes[0], is_extended, extend_line->is_extended); return 0; } static void rkcif_check_buffer_update_pingpong(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_buffer *buffer = NULL; struct rkcif_dummy_buffer *dummy_buf = &dev->dummy_buf; u32 frm_addr_y, frm_addr_uv; unsigned long flags; if (stream->state == RKCIF_STATE_STREAMING && stream->curr_buf == stream->next_buf && stream->cif_fmt_in->field != V4L2_FIELD_INTERLACED && (!dummy_buf->vaddr)) { if (!stream->is_line_wake_up) { if (mbus_cfg->type == V4L2_MBUS_CSI2 || 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) : get_reg_index_of_frm1_y_addr(channel_id); frm_addr_uv = stream->frame_phase_cache & CIF_CSI_FRAME0_READY ? get_reg_index_of_frm0_uv_addr(channel_id) : get_reg_index_of_frm1_uv_addr(channel_id); } else { frm_addr_y = stream->frame_phase_cache & CIF_CSI_FRAME0_READY ? get_dvp_reg_index_of_frm0_y_addr(channel_id) : get_dvp_reg_index_of_frm1_y_addr(channel_id); frm_addr_uv = stream->frame_phase_cache & CIF_CSI_FRAME0_READY ? get_dvp_reg_index_of_frm0_uv_addr(channel_id) : get_dvp_reg_index_of_frm1_uv_addr(channel_id); } v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "stream[%d] update buf in %s, stream->frame_phase_cache %d\n", stream->id, __func__, stream->frame_phase_cache); spin_lock_irqsave(&stream->vbq_lock, flags); if (!list_empty(&stream->buf_head)) { if (stream->frame_phase_cache == CIF_CSI_FRAME0_READY) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->curr_buf) { list_del(&stream->curr_buf->queue); buffer = stream->curr_buf; } } else if (stream->frame_phase_cache == CIF_CSI_FRAME1_READY) { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->next_buf) { list_del(&stream->next_buf->queue); buffer = stream->next_buf; } } } spin_unlock_irqrestore(&stream->vbq_lock, flags); if (buffer) { rkcif_write_register(dev, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]); if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW) rkcif_write_register(dev, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_CBCR]); } } else { spin_lock_irqsave(&stream->vbq_lock, flags); if (stream->curr_buf == stream->next_buf) { if (stream->frame_phase_cache == CIF_CSI_FRAME0_READY) { stream->curr_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->curr_buf) list_del(&stream->curr_buf->queue); } else if (stream->frame_phase_cache == CIF_CSI_FRAME1_READY) { stream->next_buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); if (stream->next_buf) list_del(&stream->next_buf->queue); } stream->is_buf_active = true; } spin_unlock_irqrestore(&stream->vbq_lock, flags); } } } /* * The vb2_buffer are stored in rkcif_buffer, in order to unify * mplane buffer and none-mplane buffer. */ static void rkcif_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_stream *stream = queue->drv_priv; struct v4l2_pix_format_mplane *pixm = &stream->pixm; const struct cif_output_fmt *fmt = stream->cif_fmt_out; struct rkcif_hw *hw_dev = stream->cifdev->hw_dev; unsigned long flags; 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, &stream->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(&stream->vbq_lock, flags); list_add_tail(&cifbuf->queue, &stream->buf_head); spin_unlock_irqrestore(&stream->vbq_lock, flags); if (stream->cifdev->workmode == RKCIF_WORKMODE_PINGPONG) rkcif_check_buffer_update_pingpong(stream, stream->id); v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "stream[%d] buf queue, index: %d\n", stream->id, vb->index); } 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; /* get a maximum plane size */ dummy_buf->size = max3(stream->pixm.plane_fmt[0].bytesperline * stream->pixm.height, stream->pixm.plane_fmt[1].sizeimage, stream->pixm.plane_fmt[2].sizeimage); /* * rk cif don't support output yuyv fmt data * if user request yuyv fmt, the input mode must be RAW8 * and the width is double Because the real input fmt is * yuyv */ fourcc = stream->cif_fmt_out->fourcc; if (fourcc == V4L2_PIX_FMT_YUYV || fourcc == V4L2_PIX_FMT_YVYU || 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) { v4l2_err(&dev->v4l2_dev, "Failed to allocate the memory for dummy buffer\n"); return -ENOMEM; } v4l2_info(&dev->v4l2_dev, "Allocate dummy buffer, size: 0x%08x\n", dummy_buf->size); return 0; } 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); dummy_buf->dma_addr = 0; dummy_buf->vaddr = NULL; } static void rkcif_do_cru_reset(struct rkcif_device *dev) { struct rkcif_hw *cif_hw = dev->hw_dev; unsigned int val, i; if (dev->luma_vdev.enable) rkcif_stop_luma(&dev->luma_vdev); if (dev->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; rkcif_write_register(dev, CIF_REG_MIPI_WATER_LINE, val); } else { val = rkcif_read_register(dev, CIF_REG_MIPI_LVDS_CTRL); val |= CIF_MIPI_LVDS_SW_DMA_IDLE; rkcif_write_register(dev, CIF_REG_MIPI_LVDS_CTRL, val); } udelay(5); } for (i = 0; i < ARRAY_SIZE(cif_hw->cif_rst); i++) if (cif_hw->cif_rst[i]) reset_control_assert(cif_hw->cif_rst[i]); udelay(10); for (i = 0; i < ARRAY_SIZE(cif_hw->cif_rst); i++) if (cif_hw->cif_rst[i]) reset_control_deassert(cif_hw->cif_rst[i]); if (cif_hw->iommu_en) { struct iommu_domain *domain; domain = iommu_get_domain_for_dev(cif_hw->dev); if (domain) { iommu_detach_device(domain, cif_hw->dev); iommu_attach_device(domain, cif_hw->dev); } } } static void rkcif_release_rdbk_buf(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_buffer *rdbk_buf; struct rkcif_buffer *tmp_buf; unsigned long flags; bool has_added; int index = 0; if (stream->id == RKCIF_STREAM_MIPI_ID0) index = RDBK_L; else if (stream->id == RKCIF_STREAM_MIPI_ID1) index = RDBK_M; else if (stream->id == RKCIF_STREAM_MIPI_ID2) index = RDBK_S; else return; spin_lock_irqsave(&dev->hdr_lock, flags); rdbk_buf = dev->rdbk_buf[index]; if (rdbk_buf) { if (rdbk_buf != stream->curr_buf && rdbk_buf != stream->next_buf) { has_added = false; list_for_each_entry(tmp_buf, &stream->buf_head, queue) { if (tmp_buf == rdbk_buf) { has_added = true; break; } } if (!has_added) list_add_tail(&rdbk_buf->queue, &stream->buf_head); } dev->rdbk_buf[index] = NULL; } spin_unlock_irqrestore(&dev->hdr_lock, flags); } static void rkcif_stop_streaming(struct vb2_queue *queue) { 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; struct rkcif_buffer *buf = NULL; int ret; struct rkcif_hw *hw_dev = dev->hw_dev; bool can_reset = true; int i; unsigned long flags; mutex_lock(&dev->hw_dev->dev_lock); v4l2_info(&dev->v4l2_dev, "stream[%d] start stopping\n", stream->id); 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; } } 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; } } else { dev->can_be_reset = true; } spin_lock_irqsave(&hw_dev->hw_timer.timer_lock, flags); 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 && can_reset) { rkcif_do_cru_reset(dev); dev->can_be_reset = false; hw_dev->reset_work_cancel = true; hw_dev->hw_timer.is_running = false; hw_dev->reset_info.is_need_reset = 0; } spin_unlock_irqrestore(&hw_dev->hw_timer.timer_lock, flags); if (!atomic_read(&dev->pipe.stream_cnt) && dev->dummy_buf.vaddr) rkcif_destroy_dummy_buf(stream); v4l2_info(&dev->v4l2_dev, "stream[%d] stopping finished\n", stream->id); mutex_unlock(&dev->hw_dev->dev_lock); } /* * CIF supports the following input modes, * YUV, the default mode * PAL, * NTSC, * RAW, if the input format is raw bayer * JPEG, TODO * MIPI, TODO */ static u32 rkcif_determine_input_mode(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: mode = INPUT_MODE_NTSC; break; 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); } } else { /* determine input mode by mbus_code (fmt_type) */ switch (stream->cif_fmt_in->fmt_type) { case CIF_FMT_TYPE_YUV: if (dev->chip_id >= CHIP_RK3568_CIF) { if (sensor_info->mbus.type == V4L2_MBUS_BT656) mode = INPUT_MODE_BT656_YUV422; else mode = INPUT_MODE_YUV; } else { mode = INPUT_MODE_YUV; } break; case CIF_FMT_TYPE_RAW: if (dev->chip_id >= CHIP_RK3568_CIF) { ret = v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_BT656_INTF_TYPE, &intf); if (!ret) { if (intf == BT656_SONY_RAW) mode = INPUT_MODE_SONY_RAW; else mode = INPUT_MODE_RAW; } else { mode = INPUT_MODE_RAW; } } else { mode = INPUT_MODE_RAW; } break; } } return mode; } static inline u32 rkcif_scl_ctl(struct rkcif_stream *stream) { u32 fmt_type = stream->cif_fmt_in->fmt_type; return (fmt_type == CIF_FMT_TYPE_YUV) ? ENABLE_YUV_16BIT_BYPASS : ENABLE_RAW_16BIT_BYPASS; } /** * rkcif_align_bits_per_pixel() - return the bit width of per pixel for stored * In raw or jpeg mode, data is stored by 16-bits,so need to align it. */ static u32 rkcif_align_bits_per_pixel(struct rkcif_stream *stream, const struct cif_output_fmt *fmt, int plane_index) { u32 bpp = 0, i, cal = 0; if (fmt) { switch (fmt->fourcc) { case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y16: bpp = fmt->bpp[plane_index]; break; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_BGR666: 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_SBGGR16: case V4l2_PIX_FMT_SPD16: case V4l2_PIX_FMT_EBD8: if (stream->cifdev->chip_id < CHIP_RV1126_CIF) { bpp = max(fmt->bpp[plane_index], (u8)CIF_RAW_STORED_BIT_WIDTH); cal = CIF_RAW_STORED_BIT_WIDTH; } else { bpp = max(fmt->bpp[plane_index], (u8)CIF_RAW_STORED_BIT_WIDTH_RV1126); cal = CIF_RAW_STORED_BIT_WIDTH_RV1126; } for (i = 1; i < 5; i++) { if (i * cal >= bpp) { bpp = i * cal; break; } } break; default: pr_err("fourcc: %d is not supported!\n", fmt->fourcc); break; } } return bpp; } /** * rkcif_cal_raw_vir_line_ratio() - return ratio for virtual line width setting * In raw or jpeg mode, data is stored by 16-bits, * so need to align virtual line width. */ static u32 rkcif_cal_raw_vir_line_ratio(struct rkcif_stream *stream, const struct cif_output_fmt *fmt) { u32 ratio = 0, bpp = 0; if (fmt) { bpp = rkcif_align_bits_per_pixel(stream, fmt, 0); ratio = bpp / CIF_YUV_STORED_BIT_WIDTH; } return ratio; } static void rkcif_sync_crop_info(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct v4l2_subdev_selection input_sel; int ret; if (dev->terminal_sensor.sd) { input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; ret = v4l2_subdev_call(dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); if (!ret) { stream->crop[CROP_SRC_SENSOR] = input_sel.r; stream->crop_enable = true; stream->crop_mask |= CROP_SRC_SENSOR_MASK; dev->terminal_sensor.selection = input_sel; } else { dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect; } } if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) { if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width > stream->crop[CROP_SRC_SENSOR].width || stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height > stream->crop[CROP_SRC_SENSOR].height) stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR]; } if (stream->crop_mask & CROP_SRC_USR_MASK) { stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR]; if (stream->crop_mask & CROP_SRC_SENSOR_MASK) { stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_SENSOR].left; stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_SENSOR].top; } } else { stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR]; } } /**rkcif_sanity_check_fmt - check fmt for setting * @stream - the stream for setting * @s_crop - the crop information */ static int rkcif_sanity_check_fmt(struct rkcif_stream *stream, const struct v4l2_rect *s_crop) { struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct v4l2_rect input, *crop; int vc; if (dev->terminal_sensor.sd) { stream->cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd, &input, stream->id, &vc); if (!stream->cif_fmt_in) { v4l2_err(v4l2_dev, "Input fmt is invalid\n"); return -EINVAL; } } else { v4l2_err(v4l2_dev, "terminal_sensor is invalid\n"); return -EINVAL; } stream->vc = vc; if (stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8 || stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8) { stream->crop_enable = false; return 0; } if (s_crop) crop = (struct v4l2_rect *)s_crop; else crop = &stream->crop[CROP_SRC_ACT]; if (crop->width + crop->left > input.width || crop->height + crop->top > input.height) { v4l2_err(v4l2_dev, "crop size is bigger than input\n"); return -EINVAL; } if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { if (crop->left > 0) { int align_x = get_csi_crop_align(stream->cif_fmt_in); if (align_x > 0 && crop->left % align_x != 0) { v4l2_err(v4l2_dev, "ERROR: crop left must align %d\n", align_x); return -EINVAL; } } } else if (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); return -EINVAL; } } return 0; } int rkcif_update_sensor_info(struct rkcif_stream *stream) { struct rkcif_sensor_info *sensor, *terminal_sensor; struct v4l2_subdev *sensor_sd; int ret = 0; sensor_sd = get_remote_sensor(stream, NULL); if (!sensor_sd) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: stream[%d] get remote sensor_sd failed!\n", __func__, stream->id); return -ENODEV; } sensor = sd_to_sensor(stream->cifdev, sensor_sd); if (!sensor) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: stream[%d] get remote sensor failed!\n", __func__, stream->id); return -ENODEV; } ret = v4l2_subdev_call(sensor->sd, video, g_mbus_config, &sensor->mbus); if (ret && ret != -ENOIOCTLCMD) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: get remote %s mbus failed!\n", __func__, sensor->sd->name); return ret; } stream->cifdev->active_sensor = sensor; terminal_sensor = &stream->cifdev->terminal_sensor; get_remote_terminal_sensor(stream, &terminal_sensor->sd); if (terminal_sensor->sd) { ret = v4l2_subdev_call(terminal_sensor->sd, video, g_mbus_config, &terminal_sensor->mbus); if (ret && ret != -ENOIOCTLCMD) { v4l2_err(&stream->cifdev->v4l2_dev, "%s: get terminal %s mbus failed!\n", __func__, terminal_sensor->sd->name); return ret; } if (v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_CSI_DSI_INFO, &terminal_sensor->dsi_input_en)) { v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "%s: get terminal %s CSI/DSI sel failed, default csi input!\n", __func__, terminal_sensor->sd->name); terminal_sensor->dsi_input_en = 0; } } if (terminal_sensor->mbus.type == V4L2_MBUS_CSI2 || terminal_sensor->mbus.type == V4L2_MBUS_CCP2) { switch (terminal_sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) { case V4L2_MBUS_CSI2_1_LANE: terminal_sensor->lanes = 1; break; case V4L2_MBUS_CSI2_2_LANE: terminal_sensor->lanes = 2; break; case V4L2_MBUS_CSI2_3_LANE: terminal_sensor->lanes = 3; break; case V4L2_MBUS_CSI2_4_LANE: terminal_sensor->lanes = 4; break; default: v4l2_err(&stream->cifdev->v4l2_dev, "%s:get sd:%s lane num failed!\n", __func__, terminal_sensor->sd ? terminal_sensor->sd->name : "null"); return -EINVAL; } } return ret; } static int rkcif_stream_start(struct rkcif_stream *stream) { u32 val, mbus_flags, href_pol, vsync_pol, xfer_mode = 0, yc_swap = 0, inputmode = 0, mipimode = 0, workmode = 0, multi_id = 0, multi_id_en = BT656_1120_MULTI_ID_DISABLE, 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; 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; if (stream->state != RKCIF_STATE_RESET_IN_STREAMING) stream->frame_idx = 0; sensor_info = dev->active_sensor; mbus = &sensor_info->mbus; if (sensor_info->sd && mbus->type == V4L2_MBUS_BT656) { int ret; multi_id_en = BT656_1120_MULTI_ID_ENABLE; ret = v4l2_subdev_call(sensor_info->sd, core, ioctl, RKMODULE_GET_BT656_MBUS_INFO, &bt1120_info); if (ret) { v4l2_warn(&dev->v4l2_dev, "waring: no muti channel info for BT.656\n"); } else { bt1120_flags = bt1120_info.flags; if (bt1120_flags & RKMODULE_CAMERA_BT656_PARSE_ID_LSB) multi_id_sel = BT656_1120_MULTI_ID_SEL_LSB; else multi_id_sel = BT656_1120_MULTI_ID_SEL_MSB; if (((bt1120_flags & RKMODULE_CAMERA_BT656_CHANNELS) >> 2) > 3) multi_id_mode = BT656_1120_MULTI_ID_MODE_4; else if (((bt1120_flags & RKMODULE_CAMERA_BT656_CHANNELS) >> 2) > 1) multi_id_mode = BT656_1120_MULTI_ID_MODE_2; multi_id = DVP_SW_MULTI_ID(stream->id, stream->id, bt1120_info.id_en_bits); rkcif_write_register_or(dev, CIF_REG_DVP_MULTI_ID, multi_id); } } mbus_flags = mbus->flags; if ((mbus_flags & CIF_DVP_PCLK_DUAL_EDGE) == CIF_DVP_PCLK_DUAL_EDGE) { bt1120_edge_mode = BT1120_CLOCK_DOUBLE_EDGES; rkcif_enable_dvp_clk_dual_edge(dev, true); } else { bt1120_edge_mode = BT1120_CLOCK_SINGLE_EDGES; rkcif_enable_dvp_clk_dual_edge(dev, false); } if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) rkcif_config_dvp_clk_sampling_edge(dev, RKCIF_CLK_RISING); else rkcif_config_dvp_clk_sampling_edge(dev, RKCIF_CLK_FALLING); href_pol = (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ? HSY_HIGH_ACTIVE : HSY_LOW_ACTIVE; 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_RK1808_CIF) { if (inputmode == 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->dvp_fmt_val)) yc_swap = BT1120_YC_SWAP; } } else { if (sensor_info->mbus.type == V4L2_MBUS_BT656) { if (stream->cif_fmt_in->field == V4L2_FIELD_NONE) xfer_mode = BT1120_TRANSMIT_PROGRESS; else xfer_mode = BT1120_TRANSMIT_INTERFACE; } if (inputmode == INPUT_MODE_BT1120) { if (CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->dvp_fmt_val)) yc_swap = BT1120_YC_SWAP; } } if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2) { inputmode = INPUT_MODE_MIPI; /* if cif is linked with mipi, * href pol must be set as high active, * vsyn pol must be set as low active. */ href_pol = HSY_HIGH_ACTIVE; vsync_pol = VSY_LOW_ACTIVE; if (stream->cif_fmt_in->fmt_type == CIF_FMT_TYPE_YUV) mipimode = MIPI_MODE_YUV; else if (stream->cif_fmt_in->fmt_type == CIF_FMT_TYPE_RAW) mipimode = MIPI_MODE_RGB; else 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; rkcif_write_register(dev, CIF_REG_DVP_FOR, val); val = stream->pixm.width; if (stream->cif_fmt_in->fmt_type == CIF_FMT_TYPE_RAW) { fmt = find_output_fmt(stream, stream->pixm.pixelformat); if (fmt->fourcc == V4L2_PIX_FMT_GREY || fmt->fourcc == V4L2_PIX_FMT_SRGGB8 || fmt->fourcc == V4L2_PIX_FMT_SGRBG8 || fmt->fourcc == V4L2_PIX_FMT_SGBRG8 || fmt->fourcc == V4L2_PIX_FMT_SBGGR8) val = ALIGN(stream->pixm.width * fmt->raw_bpp / 8, 256); else val = stream->pixm.width * rkcif_cal_raw_vir_line_ratio(stream, fmt); } rkcif_write_register(dev, CIF_REG_DVP_VIR_LINE_WIDTH, val); rkcif_write_register(dev, CIF_REG_DVP_SET_SIZE, stream->pixm.width | (stream->pixm.height << 16)); if (stream->crop_enable) { dev->channels[stream->id].crop_en = 1; dev->channels[stream->id].crop_st_x = stream->crop[CROP_SRC_ACT].left; dev->channels[stream->id].crop_st_y = stream->crop[CROP_SRC_ACT].top; dev->channels[stream->id].width = stream->crop[CROP_SRC_ACT].width; dev->channels[stream->id].height = stream->crop[CROP_SRC_ACT].height; } else { dev->channels[stream->id].crop_st_y = 0; dev->channels[stream->id].crop_st_x = 0; dev->channels[stream->id].width = stream->pixm.width; dev->channels[stream->id].height = stream->pixm.height; dev->channels[stream->id].crop_en = 0; } rkcif_write_register(dev, CIF_REG_DVP_CROP, dev->channels[stream->id].crop_st_y << CIF_CROP_Y_SHIFT | dev->channels[stream->id].crop_st_x); 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); 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); if (dev->workmode == RKCIF_WORKMODE_ONEFRAME) workmode = MODE_ONEFRAME; else if (dev->workmode == RKCIF_WORKMODE_PINGPONG) workmode = MODE_PINGPONG; else workmode = MODE_LINELOOP; if (inputmode == INPUT_MODE_BT1120) { workmode = MODE_PINGPONG; dev->workmode = RKCIF_WORKMODE_PINGPONG; } rkcif_write_register(dev, CIF_REG_DVP_CTRL, AXI_BURST_16 | workmode | ENABLE_CAPTURE); atomic_set(&sof_sd->frm_sync_seq, 0); stream->state = RKCIF_STATE_STREAMING; stream->cifdev->dvp_sof_in_oneframe = 0; return 0; } static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count) { 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; struct rkcif_sensor_info *sensor_info = dev->active_sensor; struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; struct rkmodule_hdr_cfg hdr_cfg; int rkmodule_stream_seq = RKMODULE_START_STREAM_DEFAULT; int ret; v4l2_info(&dev->v4l2_dev, "stream[%d] start streaming\n", stream->id); mutex_lock(&dev->hw_dev->dev_lock); if (WARN_ON(stream->state != RKCIF_STATE_READY)) { ret = -EBUSY; v4l2_err(v4l2_dev, "stream in busy state\n"); goto destroy_buf; } 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); if (ret < 0) { v4l2_err(v4l2_dev, "update sensor info failed %d\n", ret); goto out; } } if (terminal_sensor->sd) { ret = v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) dev->hdr.mode = hdr_cfg.hdr_mode; else dev->hdr.mode = NO_HDR; ret = v4l2_subdev_call(terminal_sensor->sd, video, g_frame_interval, &terminal_sensor->fi); if (ret) terminal_sensor->fi.interval = (struct v4l2_fract) {1, 30}; ret = v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_START_STREAM_SEQ, &rkmodule_stream_seq); if (ret) rkmodule_stream_seq = RKMODULE_START_STREAM_DEFAULT; rkcif_sync_crop_info(stream); } ret = rkcif_sanity_check_fmt(stream, NULL); if (ret < 0) goto destroy_buf; if (((dev->active_sensor && dev->active_sensor->mbus.type == V4L2_MBUS_BT656) || dev->is_use_dummybuf) && (!dev->dummy_buf.vaddr)) { ret = rkcif_create_dummy_buf(stream); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to create dummy_buf, %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; } if (dev->chip_id >= CHIP_RK1808_CIF) { if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2 || dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) ret = rkcif_csi_stream_start(stream); else ret = rkcif_stream_start(stream); } else { ret = rkcif_stream_start(stream); } 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 (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, 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); } } if (dev->hw_dev->reset_work_cancel) dev->hw_dev->reset_work_cancel = false; if (dev->hdr.mode == NO_HDR) stream->streamon_timestamp = ktime_get_ns(); goto out; stop_stream: rkcif_stream_stop(stream); pipe_stream_off: dev->pipe.set_stream(&dev->pipe, false); runtime_put: pm_runtime_put_sync(dev->dev); destroy_buf: if (stream->next_buf) vb2_buffer_done(&stream->next_buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); if (stream->curr_buf) vb2_buffer_done(&stream->curr_buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); while (!list_empty(&stream->buf_head)) { struct rkcif_buffer *buf; buf = list_first_entry(&stream->buf_head, struct rkcif_buffer, queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); list_del(&buf->queue); } out: mutex_unlock(&dev->hw_dev->dev_lock); return ret; } static struct vb2_ops rkcif_vb2_ops = { .queue_setup = rkcif_queue_setup, .buf_queue = rkcif_buf_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .stop_streaming = rkcif_stop_streaming, .start_streaming = rkcif_start_streaming, }; static int rkcif_init_vb2_queue(struct vb2_queue *q, struct rkcif_stream *stream, enum v4l2_buf_type buf_type) { struct rkcif_hw *hw_dev = stream->cifdev->hw_dev; q->type = buf_type; q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = stream; q->ops = &rkcif_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); if (stream->cifdev->is_use_dummybuf) q->min_buffers_needed = 1; else q->min_buffers_needed = CIF_REQ_BUFS_MIN; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &stream->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 void rkcif_set_fmt(struct rkcif_stream *stream, struct v4l2_pix_format_mplane *pixm, bool try) { struct rkcif_device *dev = stream->cifdev; const struct cif_output_fmt *fmt; const struct cif_input_fmt *cif_fmt_in = NULL; struct v4l2_rect input_rect; unsigned int imagesize = 0, ex_imagesize = 0, planes; u32 xsubs = 1, ysubs = 1, i; struct rkmodule_hdr_cfg hdr_cfg; struct rkcif_extend_info *extend_line = &stream->extend_line; int ret, vc; fmt = find_output_fmt(stream, pixm->pixelformat); if (!fmt) fmt = &out_fmts[0]; input_rect.width = RKCIF_DEFAULT_WIDTH; input_rect.height = RKCIF_DEFAULT_HEIGHT; if (dev->terminal_sensor.sd) { cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd, &input_rect, stream->id, &vc); stream->cif_fmt_in = cif_fmt_in; } if (dev->terminal_sensor.sd) { ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) dev->hdr.mode = hdr_cfg.hdr_mode; else dev->hdr.mode = NO_HDR; dev->terminal_sensor.raw_rect = input_rect; } /* CIF has not scale function, * the size should not be larger than input */ pixm->width = clamp_t(u32, pixm->width, CIF_MIN_WIDTH, input_rect.width); pixm->height = clamp_t(u32, pixm->height, CIF_MIN_HEIGHT, input_rect.height); pixm->num_planes = fmt->mplanes; pixm->field = V4L2_FIELD_NONE; pixm->quantization = V4L2_QUANTIZATION_DEFAULT; rkcif_sync_crop_info(stream); /* calculate plane size and image size */ fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; for (i = 0; i < planes; i++) { struct v4l2_plane_pix_format *plane_fmt; int width, height, bpl, size, bpp, ex_size; if (i == 0) { if (stream->crop_enable) { width = stream->crop[CROP_SRC_ACT].width; height = stream->crop[CROP_SRC_ACT].height; } else { width = pixm->width; height = pixm->height; } } else { if (stream->crop_enable) { width = stream->crop[CROP_SRC_ACT].width / xsubs; height = stream->crop[CROP_SRC_ACT].height / ysubs; } else { width = pixm->width / xsubs; height = pixm->height / ysubs; } } extend_line->pixm.height = height + RKMODULE_EXTEND_LINE; /* compact mode need bytesperline 4bytes align, * align 8 to bring into correspondence with virtual width. * to optimize reading and writing of ddr, aliged with 256. */ if (fmt->fmt_type == CIF_FMT_TYPE_RAW && (stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8 || stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8)) { stream->is_compact = false; } if (fmt->fmt_type == CIF_FMT_TYPE_RAW && stream->is_compact && (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2 || dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) && fmt->csi_fmt_val != CSI_WRDDR_TYPE_RGB888) { bpl = ALIGN(width * fmt->raw_bpp / 8, 256); } else if (fmt->fourcc == V4L2_PIX_FMT_GREY || fmt->fourcc == V4L2_PIX_FMT_SRGGB8 || fmt->fourcc == V4L2_PIX_FMT_SGRBG8 || fmt->fourcc == V4L2_PIX_FMT_SGBRG8 || fmt->fourcc == V4L2_PIX_FMT_SBGGR8) { bpl = ALIGN(width * fmt->raw_bpp / 8, 256); } else { bpp = rkcif_align_bits_per_pixel(stream, fmt, i); bpl = width * bpp / CIF_YUV_STORED_BIT_WIDTH; } size = bpl * height; imagesize += size; ex_size = bpl * extend_line->pixm.height; ex_imagesize += ex_size; if (fmt->mplanes > i) { /* Set bpl and size for each mplane */ plane_fmt = pixm->plane_fmt + i; plane_fmt->bytesperline = bpl; plane_fmt->sizeimage = size; plane_fmt = extend_line->pixm.plane_fmt + i; plane_fmt->bytesperline = bpl; plane_fmt->sizeimage = ex_size; } v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "C-Plane %i size: %d, Total imagesize: %d\n", i, size, imagesize); } /* convert to non-MPLANE format. * It's important since we want to unify non-MPLANE * and MPLANE. */ if (fmt->mplanes == 1) { pixm->plane_fmt[0].sizeimage = imagesize; extend_line->pixm.plane_fmt[0].sizeimage = ex_imagesize; } if (!try) { stream->cif_fmt_out = fmt; stream->pixm = *pixm; v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "%s: req(%d, %d) out(%d, %d)\n", __func__, pixm->width, pixm->height, stream->pixm.width, stream->pixm.height); } } void rkcif_stream_init(struct rkcif_device *dev, u32 id) { struct rkcif_stream *stream = &dev->stream[id]; struct v4l2_pix_format_mplane pixm; int i; memset(stream, 0, sizeof(*stream)); memset(&pixm, 0, sizeof(pixm)); stream->id = id; stream->cifdev = dev; INIT_LIST_HEAD(&stream->buf_head); spin_lock_init(&stream->vbq_lock); spin_lock_init(&stream->fps_lock); stream->state = RKCIF_STATE_READY; init_waitqueue_head(&stream->wq_stopped); /* Set default format */ pixm.pixelformat = V4L2_PIX_FMT_NV12; pixm.width = RKCIF_DEFAULT_WIDTH; pixm.height = RKCIF_DEFAULT_HEIGHT; rkcif_set_fmt(stream, &pixm, false); for (i = 0; i < CROP_SRC_MAX; i++) { stream->crop[i].left = 0; stream->crop[i].top = 0; stream->crop[i].width = RKCIF_DEFAULT_WIDTH; stream->crop[i].height = RKCIF_DEFAULT_HEIGHT; } stream->crop_enable = false; stream->crop_dyn_en = false; stream->crop_mask = 0x0; if (dev->inf_id == RKCIF_DVP) { if (dev->chip_id <= CHIP_RK3568_CIF) stream->is_compact = false; else stream->is_compact = true; } else { if (dev->chip_id >= CHIP_RV1126_CIF) stream->is_compact = true; else stream->is_compact = false; } stream->is_high_align = false; if (dev->chip_id == CHIP_RV1126_CIF || dev->chip_id == CHIP_RV1126_CIF_LITE) stream->extend_line.is_extended = true; else stream->extend_line.is_extended = false; stream->is_dvp_yuv_addr_init = false; stream->is_fs_fe_not_paired = false; stream->fs_cnt_in_single_frame = 0; if (dev->wait_line) { dev->wait_line_cache = dev->wait_line; dev->wait_line_bak = dev->wait_line; stream->is_line_wake_up = true; } else { stream->is_line_wake_up = false; dev->wait_line_cache = 0; dev->wait_line_bak = 0; } } static int rkcif_fh_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct rkcif_vdev_node *vnode = vdev_to_node(vdev); struct rkcif_stream *stream = to_rkcif_stream(vnode); struct rkcif_device *cifdev = stream->cifdev; int ret; ret = rkcif_attach_hw(cifdev); if (ret) return ret; /* Make sure active sensor is valid before .set_fmt() */ ret = rkcif_update_sensor_info(stream); if (ret < 0) { v4l2_err(vdev, "update sensor info failed %d\n", ret); return ret; } /* enable clocks/power-domains */ ret = pm_runtime_get_sync(cifdev->dev); if (ret < 0) { v4l2_err(vdev, "Failed to get runtime pm, %d\n", ret); return ret; } /* * Soft reset via CRU. * Because CRU would reset iommu too, so there's not chance * to reset cif once we hold buffers after buf queued */ 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) { mutex_lock(&cifdev->stream_lock); if (!atomic_read(&cifdev->fh_cnt)) rkcif_soft_reset(cifdev, true); atomic_inc(&cifdev->fh_cnt); mutex_unlock(&cifdev->stream_lock); } else { rkcif_soft_reset(cifdev, true); } ret = v4l2_fh_open(filp); if (!ret) { ret = v4l2_pipeline_pm_use(&vnode->vdev.entity, 1); if (ret < 0) vb2_fop_release(filp); } return ret; } static int rkcif_fh_release(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct rkcif_vdev_node *vnode = vdev_to_node(vdev); struct rkcif_stream *stream = to_rkcif_stream(vnode); struct rkcif_device *cifdev = stream->cifdev; int ret = 0; ret = vb2_fop_release(filp); if (!ret) { ret = v4l2_pipeline_pm_use(&vnode->vdev.entity, 0); if (ret < 0) v4l2_err(&cifdev->v4l2_dev, "set pipeline power failed %d\n", ret); } mutex_lock(&cifdev->stream_lock); if (!atomic_dec_return(&cifdev->fh_cnt)) rkcif_soft_reset(cifdev, true); else if (atomic_read(&cifdev->fh_cnt) < 0) atomic_set(&cifdev->fh_cnt, 0); mutex_unlock(&cifdev->stream_lock); pm_runtime_put_sync(cifdev->dev); return ret; } static const struct v4l2_file_operations rkcif_fops = { .open = rkcif_fh_open, .release = rkcif_fh_release, .unlocked_ioctl = video_ioctl2, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, #ifdef CONFIG_COMPAT .compat_ioctl32 = video_ioctl2, #endif }; static int rkcif_enum_input(struct file *file, void *priv, struct v4l2_input *input) { if (input->index > 0) return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; strlcpy(input->name, "Camera", sizeof(input->name)); return 0; } static int rkcif_try_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkcif_stream *stream = video_drvdata(file); rkcif_set_fmt(stream, &f->fmt.pix_mp, true); return 0; } static int rkcif_enum_framesizes(struct file *file, void *prov, struct v4l2_frmsizeenum *fsize) { struct v4l2_frmsize_stepwise *s = &fsize->stepwise; struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; struct v4l2_rect input_rect; int vc; if (fsize->index != 0) return -EINVAL; if (!find_output_fmt(stream, fsize->pixel_format)) return -EINVAL; input_rect.width = RKCIF_DEFAULT_WIDTH; input_rect.height = RKCIF_DEFAULT_HEIGHT; if (dev->terminal_sensor.sd) get_input_fmt(dev->terminal_sensor.sd, &input_rect, stream->id, &vc); fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; s->min_width = CIF_MIN_WIDTH; s->min_height = CIF_MIN_HEIGHT; s->max_width = input_rect.width; s->max_height = input_rect.height; s->step_width = OUTPUT_STEP_WISE; s->step_height = OUTPUT_STEP_WISE; return 0; } static int rkcif_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *sensor = dev->active_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_enum_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { const struct cif_output_fmt *fmt = NULL; if (f->index >= ARRAY_SIZE(out_fmts)) return -EINVAL; fmt = &out_fmts[f->index]; f->pixelformat = fmt->fourcc; return 0; } static int rkcif_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; if (vb2_is_busy(&stream->vnode.buf_queue)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } rkcif_set_fmt(stream, &f->fmt.pix_mp, false); return 0; } static int rkcif_g_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkcif_stream *stream = video_drvdata(file); f->fmt.pix_mp = stream->pixm; return 0; } static int rkcif_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct rkcif_stream *stream = video_drvdata(file); struct device *dev = stream->cifdev->dev; strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(dev)); return 0; } static int rkcif_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cap) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; struct v4l2_rect *raw_rect = &dev->terminal_sensor.raw_rect; int ret = 0; if (stream->crop_mask & CROP_SRC_SENSOR) { cap->bounds.left = stream->crop[CROP_SRC_SENSOR].left; cap->bounds.top = stream->crop[CROP_SRC_SENSOR].top; cap->bounds.width = stream->crop[CROP_SRC_SENSOR].width; cap->bounds.height = stream->crop[CROP_SRC_SENSOR].height; } else { cap->bounds.left = raw_rect->left; cap->bounds.top = raw_rect->top; cap->bounds.width = raw_rect->width; cap->bounds.height = raw_rect->height; } cap->defrect = cap->bounds; cap->pixelaspect.numerator = 1; cap->pixelaspect.denominator = 1; return ret; } static int rkcif_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; const struct v4l2_rect *rect = &a->c; struct v4l2_rect sensor_crop; struct v4l2_rect *raw_rect = &dev->terminal_sensor.raw_rect; int ret; ret = rkcif_sanity_check_fmt(stream, rect); if (ret) { v4l2_err(&dev->v4l2_dev, "set crop failed\n"); return ret; } if (stream->crop_mask & CROP_SRC_SENSOR) { sensor_crop = stream->crop[CROP_SRC_SENSOR]; if (rect->left + rect->width > sensor_crop.width || rect->top + rect->height > sensor_crop.height) { v4l2_err(&dev->v4l2_dev, "crop size is bigger than sensor input:left:%d, top:%d, width:%d, height:%d\n", sensor_crop.left, sensor_crop.top, sensor_crop.width, sensor_crop.height); return -EINVAL; } } else { if (rect->left + rect->width > raw_rect->width || rect->top + rect->height > raw_rect->height) { v4l2_err(&dev->v4l2_dev, "crop size is bigger than sensor raw input:left:%d, top:%d, width:%d, height:%d\n", raw_rect->left, raw_rect->top, raw_rect->width, raw_rect->height); return -EINVAL; } } stream->crop[CROP_SRC_USR] = *rect; stream->crop_enable = true; stream->crop_mask |= CROP_SRC_USR_MASK; stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR]; if (stream->crop_mask & CROP_SRC_SENSOR) { stream->crop[CROP_SRC_ACT].left = sensor_crop.left + stream->crop[CROP_SRC_USR].left; stream->crop[CROP_SRC_ACT].top = sensor_crop.top + stream->crop[CROP_SRC_USR].top; } if (stream->state == RKCIF_STATE_STREAMING) { stream->crop_dyn_en = true; v4l2_info(&dev->v4l2_dev, "enable dynamic crop, S_CROP(%ux%u@%u:%u) type: %d\n", rect->width, rect->height, rect->left, rect->top, a->type); } else { v4l2_info(&dev->v4l2_dev, "static crop, S_CROP(%ux%u@%u:%u) type: %d\n", rect->width, rect->height, rect->left, rect->top, a->type); } return ret; } static int rkcif_g_crop(struct file *file, void *fh, struct v4l2_crop *a) { struct rkcif_stream *stream = video_drvdata(file); a->c = stream->crop[CROP_SRC_ACT]; return 0; } static int rkcif_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; struct v4l2_subdev *sensor_sd; struct v4l2_subdev_selection sd_sel; u16 pad = 0; int ret = 0; if (!s) { v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "sel is null\n"); goto err; } sensor_sd = get_remote_sensor(stream, &pad); sd_sel.r = s->r; sd_sel.pad = pad; sd_sel.target = s->target; sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sensor_sd, pad, set_selection, NULL, &sd_sel); if (!ret) { s->r = sd_sel.r; v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s: pad:%d, which:%d, target:%d\n", __func__, pad, sd_sel.which, sd_sel.target); } return ret; err: return -EINVAL; } static int rkcif_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; struct v4l2_subdev *sensor_sd; struct v4l2_subdev_selection sd_sel; u16 pad = 0; int ret = 0; if (!s) { v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "sel is null\n"); goto err; } if (s->target == V4L2_SEL_TGT_CROP_BOUNDS) { sensor_sd = get_remote_sensor(stream, &pad); sd_sel.pad = pad; sd_sel.target = s->target; sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s(line:%d): sd:%s pad:%d, which:%d, target:%d\n", __func__, __LINE__, sensor_sd->name, pad, sd_sel.which, sd_sel.target); ret = v4l2_subdev_call(sensor_sd, pad, get_selection, NULL, &sd_sel); if (!ret) { s->r = sd_sel.r; } else { s->r.left = 0; s->r.top = 0; s->r.width = stream->pixm.width; s->r.height = stream->pixm.height; } } else if (s->target == V4L2_SEL_TGT_CROP) { if (stream->crop_mask & (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) { s->r = stream->crop[CROP_SRC_ACT]; } else { s->r.left = 0; s->r.top = 0; s->r.width = stream->pixm.width; s->r.height = stream->pixm.height; } } else { goto err; } return ret; err: return -EINVAL; } static int rkcif_do_reset_work(struct rkcif_device *cif_dev, enum rkmodule_reset_src reset_src); static long rkcif_ioctl_default(struct file *file, void *fh, bool valid_prio, unsigned int cmd, void *arg) { struct rkcif_stream *stream = video_drvdata(file); struct rkcif_device *dev = stream->cifdev; const struct cif_input_fmt *in_fmt; struct v4l2_rect rect; int vc = 0; struct rkcif_reset_info *reset_info; int reset_src = 0; unsigned long flags; switch (cmd) { case RKCIF_CMD_GET_CSI_MEMORY_MODE: if (stream->is_compact) { *(int *)arg = CSI_LVDS_MEM_COMPACT; } else { if (stream->is_high_align) *(int *)arg = CSI_LVDS_MEM_WORD_HIGH_ALIGN; else *(int *)arg = CSI_LVDS_MEM_WORD_LOW_ALIGN; } break; case RKCIF_CMD_SET_CSI_MEMORY_MODE: if (dev->terminal_sensor.sd) { in_fmt = get_input_fmt(dev->terminal_sensor.sd, &rect, 0, &vc); if (in_fmt == NULL) { v4l2_err(&dev->v4l2_dev, "can't get sensor input format\n"); return -EINVAL; } } else { v4l2_err(&dev->v4l2_dev, "can't get sensor device\n"); return -EINVAL; } if (*(int *)arg == CSI_LVDS_MEM_COMPACT) { if (((dev->inf_id == RKCIF_DVP && dev->chip_id <= CHIP_RK3568_CIF) || (dev->inf_id == RKCIF_MIPI_LVDS && dev->chip_id < CHIP_RV1126_CIF)) && in_fmt->csi_fmt_val != CSI_WRDDR_TYPE_RAW8) { v4l2_err(&dev->v4l2_dev, "device not support compact\n"); return -EINVAL; } stream->is_compact = true; stream->is_high_align = false; } else if (*(int *)arg == CSI_LVDS_MEM_WORD_HIGH_ALIGN) { stream->is_compact = false; stream->is_high_align = true; } else { stream->is_compact = false; stream->is_high_align = false; } break; case RKCIF_CMD_GET_RESET_INFO: reset_info = (struct rkcif_reset_info *)arg; spin_lock_irqsave(&dev->hw_dev->spin_lock, flags); *reset_info = dev->hw_dev->reset_info; spin_unlock_irqrestore(&dev->hw_dev->spin_lock, flags); break; case RKCIF_CMD_SET_RESET: reset_src = *(int *)arg; spin_lock_irqsave(&dev->hw_dev->spin_lock, flags); if (dev->hw_dev->reset_info.is_need_reset) dev->hw_dev->reset_info.is_need_reset = 0; spin_unlock_irqrestore(&dev->hw_dev->spin_lock, flags); return rkcif_do_reset_work(dev, reset_src); default: return -EINVAL; } return 0; } static const struct v4l2_ioctl_ops rkcif_v4l2_ioctl_ops = { .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_qbuf = vb2_ioctl_qbuf, .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_input = rkcif_enum_input, .vidioc_try_fmt_vid_cap_mplane = rkcif_try_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_cap_mplane = rkcif_enum_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = rkcif_s_fmt_vid_cap_mplane, .vidioc_g_fmt_vid_cap_mplane = rkcif_g_fmt_vid_cap_mplane, .vidioc_querycap = rkcif_querycap, .vidioc_cropcap = rkcif_cropcap, .vidioc_s_crop = rkcif_s_crop, .vidioc_g_crop = rkcif_g_crop, .vidioc_s_selection = rkcif_s_selection, .vidioc_g_selection = rkcif_g_selection, .vidioc_enum_frameintervals = rkcif_enum_frameintervals, .vidioc_enum_framesizes = rkcif_enum_framesizes, .vidioc_default = rkcif_ioctl_default, }; static void rkcif_unregister_stream_vdev(struct rkcif_stream *stream) { media_entity_cleanup(&stream->vnode.vdev.entity); video_unregister_device(&stream->vnode.vdev); } static int rkcif_register_stream_vdev(struct rkcif_stream *stream, bool is_multi_input) { struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct video_device *vdev = &stream->vnode.vdev; struct rkcif_vdev_node *node; int ret = 0; char *vdev_name; if (dev->chip_id < CHIP_RV1126_CIF) { if (is_multi_input) { switch (stream->id) { case RKCIF_STREAM_MIPI_ID0: vdev_name = CIF_MIPI_ID0_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID1: vdev_name = CIF_MIPI_ID1_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID2: vdev_name = CIF_MIPI_ID2_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID3: vdev_name = CIF_MIPI_ID3_VDEV_NAME; break; case RKCIF_STREAM_DVP: vdev_name = CIF_DVP_VDEV_NAME; break; default: v4l2_err(v4l2_dev, "Invalid stream\n"); goto unreg; } } else { vdev_name = CIF_VIDEODEVICE_NAME; } } else { if (dev->inf_id == RKCIF_MIPI_LVDS) { switch (stream->id) { case RKCIF_STREAM_MIPI_ID0: vdev_name = CIF_MIPI_ID0_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID1: vdev_name = CIF_MIPI_ID1_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID2: vdev_name = CIF_MIPI_ID2_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID3: vdev_name = CIF_MIPI_ID3_VDEV_NAME; break; default: v4l2_err(v4l2_dev, "Invalid stream\n"); goto unreg; } } else { switch (stream->id) { case RKCIF_STREAM_MIPI_ID0: vdev_name = CIF_DVP_ID0_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID1: vdev_name = CIF_DVP_ID1_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID2: vdev_name = CIF_DVP_ID2_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID3: vdev_name = CIF_DVP_ID3_VDEV_NAME; break; default: v4l2_err(v4l2_dev, "Invalid stream\n"); goto unreg; } } } strlcpy(vdev->name, vdev_name, sizeof(vdev->name)); node = vdev_to_node(vdev); mutex_init(&node->vlock); vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops; vdev->release = video_device_release_empty; vdev->fops = &rkcif_fops; vdev->minor = -1; vdev->v4l2_dev = v4l2_dev; vdev->lock = &node->vlock; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; video_set_drvdata(vdev, stream); vdev->vfl_dir = VFL_DIR_RX; node->pad.flags = MEDIA_PAD_FL_SINK; rkcif_init_vb2_queue(&node->buf_queue, stream, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); vdev->queue = &node->buf_queue; ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { v4l2_err(v4l2_dev, "video_register_device failed with error %d\n", ret); return ret; } ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret < 0) goto unreg; return 0; unreg: video_unregister_device(vdev); return ret; } void rkcif_unregister_stream_vdevs(struct rkcif_device *dev, int stream_num) { struct rkcif_stream *stream; int i; for (i = 0; i < stream_num; i++) { stream = &dev->stream[i]; rkcif_unregister_stream_vdev(stream); } } int rkcif_register_stream_vdevs(struct rkcif_device *dev, int stream_num, bool is_multi_input) { struct rkcif_stream *stream; int i, j, ret; for (i = 0; i < stream_num; i++) { stream = &dev->stream[i]; stream->cifdev = dev; ret = rkcif_register_stream_vdev(stream, is_multi_input); if (ret < 0) goto err; } return 0; err: for (j = 0; j < i; j++) { stream = &dev->stream[j]; rkcif_unregister_stream_vdev(stream); } return ret; } static struct v4l2_subdev *get_lvds_remote_sensor(struct v4l2_subdev *sd) { struct media_pad *local, *remote; struct media_entity *sensor_me; local = &sd->entity.pads[RKCIF_LVDS_PAD_SINK]; remote = media_entity_remote_pad(local); if (!remote) { v4l2_warn(sd, "No link between dphy and sensor with lvds\n"); return NULL; } sensor_me = media_entity_remote_pad(local)->entity; return media_entity_to_v4l2_subdev(sensor_me); } static int rkcif_lvds_subdev_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { return 0; } static int rkcif_lvds_sd_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct v4l2_subdev *sensor = get_lvds_remote_sensor(sd); /* * Do not allow format changes and just relay whatever * set currently in the sensor. */ return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt); } static int rkcif_lvds_sd_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct rkcif_lvds_subdev *subdev = container_of(sd, struct rkcif_lvds_subdev, sd); struct v4l2_subdev *sensor = get_lvds_remote_sensor(sd); int ret; /* * Do not allow format changes and just relay whatever * set currently in the sensor. */ ret = v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt); if (!ret) subdev->in_fmt = fmt->format; return ret; } static struct v4l2_rect *rkcif_lvds_sd_get_crop(struct rkcif_lvds_subdev *subdev, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_crop(&subdev->sd, cfg, RKCIF_LVDS_PAD_SINK); else return &subdev->crop; } static int rkcif_lvds_sd_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct rkcif_lvds_subdev *subdev = container_of(sd, struct rkcif_lvds_subdev, sd); struct v4l2_subdev *sensor = get_lvds_remote_sensor(sd); int ret = 0; ret = v4l2_subdev_call(sensor, pad, set_selection, cfg, sel); if (!ret) subdev->crop = sel->r; return ret; } static int rkcif_lvds_sd_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct rkcif_lvds_subdev *subdev = container_of(sd, struct rkcif_lvds_subdev, sd); struct v4l2_subdev *sensor = get_lvds_remote_sensor(sd); struct v4l2_subdev_format fmt; int ret = 0; if (!sel) { v4l2_dbg(1, rkcif_debug, sd, "sel is null\n"); goto err; } if (sel->pad > RKCIF_LVDS_PAD_SRC_ID3) { v4l2_dbg(1, rkcif_debug, sd, "pad[%d] isn't matched\n", sel->pad); goto err; } switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = v4l2_subdev_call(sensor, pad, get_selection, cfg, sel); if (ret) { fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt); if (!ret) { subdev->in_fmt = fmt.format; sel->r.top = 0; sel->r.left = 0; sel->r.width = subdev->in_fmt.width; sel->r.height = subdev->in_fmt.height; subdev->crop = sel->r; } else { sel->r = subdev->crop; } } else { subdev->crop = sel->r; } } else { sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); } break; case V4L2_SEL_TGT_CROP: sel->r = *rkcif_lvds_sd_get_crop(subdev, cfg, sel->which); break; default: return -EINVAL; } return 0; err: return -EINVAL; } static int rkcif_lvds_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *mbus) { struct v4l2_subdev *sensor_sd = get_lvds_remote_sensor(sd); int ret; ret = v4l2_subdev_call(sensor_sd, video, g_mbus_config, mbus); if (ret) return ret; return 0; } static int rkcif_lvds_sd_s_stream(struct v4l2_subdev *sd, int on) { struct rkcif_lvds_subdev *subdev = container_of(sd, struct rkcif_lvds_subdev, sd); if (on) atomic_set(&subdev->frm_sync_seq, 0); return 0; } static int rkcif_lvds_sd_s_power(struct v4l2_subdev *sd, int on) { return 0; } static int rkcif_sof_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type != V4L2_EVENT_FRAME_SYNC) return -EINVAL; return v4l2_event_subscribe(fh, sub, 0, NULL); } static const struct media_entity_operations rkcif_lvds_sd_media_ops = { .link_setup = rkcif_lvds_subdev_link_setup, .link_validate = v4l2_subdev_link_validate, }; static const struct v4l2_subdev_pad_ops rkcif_lvds_sd_pad_ops = { .set_fmt = rkcif_lvds_sd_set_fmt, .get_fmt = rkcif_lvds_sd_get_fmt, .set_selection = rkcif_lvds_sd_set_selection, .get_selection = rkcif_lvds_sd_get_selection, }; static const struct v4l2_subdev_video_ops rkcif_lvds_sd_video_ops = { .g_mbus_config = rkcif_lvds_g_mbus_config, .s_stream = rkcif_lvds_sd_s_stream, }; static const struct v4l2_subdev_core_ops rkcif_lvds_sd_core_ops = { .s_power = rkcif_lvds_sd_s_power, .subscribe_event = rkcif_sof_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static struct v4l2_subdev_ops rkcif_lvds_sd_ops = { .core = &rkcif_lvds_sd_core_ops, .video = &rkcif_lvds_sd_video_ops, .pad = &rkcif_lvds_sd_pad_ops, }; static void rkcif_lvds_event_inc_sof(struct rkcif_device *dev) { struct rkcif_lvds_subdev *subdev = &dev->lvds_subdev; if (subdev) { struct v4l2_event event = { .type = V4L2_EVENT_FRAME_SYNC, .u.frame_sync.frame_sequence = atomic_inc_return(&subdev->frm_sync_seq) - 1, }; v4l2_event_queue(subdev->sd.devnode, &event); } } static u32 rkcif_lvds_get_sof(struct rkcif_device *dev) { if (dev) return atomic_read(&dev->lvds_subdev.frm_sync_seq) - 1; return 0; } static void rkcif_lvds_set_sof(struct rkcif_device *dev, u32 seq) { if (dev) atomic_set(&dev->lvds_subdev.frm_sync_seq, seq); } int rkcif_register_lvds_subdev(struct rkcif_device *dev) { struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct rkcif_lvds_subdev *lvds_subdev = &dev->lvds_subdev; struct v4l2_subdev *sd; int ret; memset(lvds_subdev, 0, sizeof(*lvds_subdev)); lvds_subdev->cifdev = dev; sd = &lvds_subdev->sd; lvds_subdev->state = RKCIF_LVDS_STOP; v4l2_subdev_init(sd, &rkcif_lvds_sd_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkcif_lvds_sd_media_ops; if (dev->chip_id == CHIP_RV1126_CIF) snprintf(sd->name, sizeof(sd->name), "rkcif-lvds-subdev"); else snprintf(sd->name, sizeof(sd->name), "rkcif-lite-lvds-subdev"); lvds_subdev->pads[RKCIF_LVDS_PAD_SINK].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; lvds_subdev->pads[RKCIF_LVDS_PAD_SRC_ID0].flags = MEDIA_PAD_FL_SOURCE; lvds_subdev->pads[RKCIF_LVDS_PAD_SRC_ID1].flags = MEDIA_PAD_FL_SOURCE; lvds_subdev->pads[RKCIF_LVDS_PAD_SRC_ID2].flags = MEDIA_PAD_FL_SOURCE; lvds_subdev->pads[RKCIF_LVDS_PAD_SRC_ID3].flags = MEDIA_PAD_FL_SOURCE; lvds_subdev->in_fmt.width = RKCIF_DEFAULT_WIDTH; lvds_subdev->in_fmt.height = RKCIF_DEFAULT_HEIGHT; lvds_subdev->crop.left = 0; lvds_subdev->crop.top = 0; lvds_subdev->crop.width = RKCIF_DEFAULT_WIDTH; lvds_subdev->crop.height = RKCIF_DEFAULT_HEIGHT; ret = media_entity_pads_init(&sd->entity, RKCIF_LVDS_PAD_MAX, lvds_subdev->pads); if (ret < 0) return ret; sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, lvds_subdev); ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret < 0) goto free_media; ret = v4l2_device_register_subdev_nodes(v4l2_dev); if (ret < 0) goto free_subdev; return ret; free_subdev: v4l2_device_unregister_subdev(sd); free_media: media_entity_cleanup(&sd->entity); v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret); return ret; } void rkcif_unregister_lvds_subdev(struct rkcif_device *dev) { struct v4l2_subdev *sd = &dev->lvds_subdev.sd; v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); } static void rkcif_dvp_event_inc_sof(struct rkcif_device *dev) { struct rkcif_dvp_sof_subdev *subdev = &dev->dvp_sof_subdev; if (subdev) { struct v4l2_event event = { .type = V4L2_EVENT_FRAME_SYNC, .u.frame_sync.frame_sequence = atomic_inc_return(&subdev->frm_sync_seq) - 1, }; v4l2_event_queue(subdev->sd.devnode, &event); } } static u32 rkcif_dvp_get_sof(struct rkcif_device *dev) { if (dev) return atomic_read(&dev->dvp_sof_subdev.frm_sync_seq) - 1; return 0; } static void rkcif_dvp_set_sof(struct rkcif_device *dev, u32 seq) { if (dev) atomic_set(&dev->dvp_sof_subdev.frm_sync_seq, seq); } static const struct v4l2_subdev_core_ops rkcif_dvp_sof_sd_core_ops = { .subscribe_event = rkcif_sof_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static struct v4l2_subdev_ops rkcif_dvp_sof_sd_ops = { .core = &rkcif_dvp_sof_sd_core_ops, }; int rkcif_register_dvp_sof_subdev(struct rkcif_device *dev) { struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct rkcif_dvp_sof_subdev *subdev = &dev->dvp_sof_subdev; struct v4l2_subdev *sd; int ret; memset(subdev, 0, sizeof(*subdev)); subdev->cifdev = dev; sd = &subdev->sd; v4l2_subdev_init(sd, &rkcif_dvp_sof_sd_ops); sd->owner = THIS_MODULE; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(sd->name, sizeof(sd->name), "rkcif-dvp-sof"); v4l2_set_subdevdata(sd, subdev); ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret < 0) goto end; ret = v4l2_device_register_subdev_nodes(v4l2_dev); if (ret < 0) goto free_subdev; return ret; free_subdev: v4l2_device_unregister_subdev(sd); end: v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret); return ret; } void rkcif_unregister_dvp_sof_subdev(struct rkcif_device *dev) { struct v4l2_subdev *sd = &dev->dvp_sof_subdev.sd; v4l2_device_unregister_subdev(sd); } static void rkcif_vb_done_oneframe(struct rkcif_stream *stream, struct vb2_v4l2_buffer *vb_done) { const struct cif_output_fmt *fmt = stream->cif_fmt_out; u32 i; /* Dequeue a filled buffer */ for (i = 0; i < fmt->mplanes; i++) { vb2_set_plane_payload(&vb_done->vb2_buf, i, stream->pixm.plane_fmt[i].sizeimage); } if (stream->cifdev->hdr.mode == NO_HDR) vb_done->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE); v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev, "stream[%d] vb done, index: %d, sequence %d\n", stream->id, vb_done->vb2_buf.index, vb_done->sequence); } void rkcif_irq_oneframe(struct rkcif_device *cif_dev) { /* TODO: xuhf-debug: add stream type */ struct rkcif_stream *stream; u32 lastline, lastpix, ctl, cif_frmst, intstat, frmid; int ret = 0; intstat = rkcif_read_register(cif_dev, CIF_REG_DVP_INTSTAT); cif_frmst = rkcif_read_register(cif_dev, CIF_REG_DVP_FRAME_STATUS); lastline = rkcif_read_register(cif_dev, CIF_REG_DVP_LAST_LINE); lastpix = rkcif_read_register(cif_dev, CIF_REG_DVP_LAST_PIX); ctl = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL); frmid = CIF_GET_FRAME_ID(cif_frmst); /* 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 */ stream = &cif_dev->stream[RKCIF_STREAM_CIF]; if ((intstat & PST_INF_FRAME_END)) { rkcif_write_register(cif_dev, CIF_REG_DVP_INTSTAT, PST_INF_FRAME_END_CLR); if (stream->stopping) /* To stop CIF ASAP, before FRAME_END irq */ rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, ctl & (~ENABLE_CAPTURE)); } if ((intstat & FRAME_END)) { struct vb2_v4l2_buffer *vb_done = NULL; rkcif_write_register(cif_dev, CIF_REG_DVP_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)) { /* Clearing status must be complete before fe packet * arrives while cif is connected with mipi, * so it should be placed before printing log here, * otherwise it would be delayed. * At the same time, don't clear the frame id * for switching address. */ rkcif_write_register(cif_dev, CIF_REG_DVP_FRAME_STATUS, FRM0_STAT_CLS); 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 (frmid % 2 != 0) { stream->frame_phase = CIF_CSI_FRAME0_READY; if (stream->curr_buf) vb_done = &stream->curr_buf->vb; } else { stream->frame_phase = CIF_CSI_FRAME1_READY; if (stream->next_buf) vb_done = &stream->next_buf->vb; } /* In one-frame mode: * 1,must clear status manually by writing 0 to enable * the next frame end irq; * 2,do not clear the frame id for switching address. */ rkcif_write_register(cif_dev, CIF_REG_DVP_FRAME_STATUS, cif_frmst & FRM0_STAT_CLS); ret = rkcif_assign_new_buffer_oneframe(stream, RKCIF_YUV_ADDR_STATE_UPDATE); if (vb_done && (!ret)) { vb_done->sequence = stream->frame_idx; rkcif_vb_done_oneframe(stream, vb_done); } stream->frame_idx++; cif_dev->irq_stats.all_frm_end_cnt++; } } static int rkcif_csi_g_mipi_id(struct v4l2_device *v4l2_dev, unsigned int intstat) { if (intstat & CSI_FRAME_END_ID0) { if ((intstat & CSI_FRAME_END_ID0) == CSI_FRAME_END_ID0) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in ID0\n"); return RKCIF_STREAM_MIPI_ID0; } if (intstat & CSI_FRAME_END_ID1) { if ((intstat & CSI_FRAME_END_ID1) == CSI_FRAME_END_ID1) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in ID1\n"); return RKCIF_STREAM_MIPI_ID1; } if (intstat & CSI_FRAME_END_ID2) { if ((intstat & CSI_FRAME_END_ID2) == CSI_FRAME_END_ID2) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in ID2\n"); return RKCIF_STREAM_MIPI_ID2; } if (intstat & CSI_FRAME_END_ID3) { if ((intstat & CSI_FRAME_END_ID3) == CSI_FRAME_END_ID3) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in ID3\n"); return RKCIF_STREAM_MIPI_ID3; } return -EINVAL; } static int rkcif_dvp_g_ch_id(struct v4l2_device *v4l2_dev, u32 *intstat, u32 frm_stat) { if (*intstat & DVP_FRAME_END_ID0) { if ((frm_stat & DVP_CHANNEL0_FRM_READ) == DVP_CHANNEL0_FRM_READ) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID0\n"); *intstat &= ~DVP_FRAME_END_ID0; return RKCIF_STREAM_MIPI_ID0; } if (*intstat & DVP_FRAME_END_ID1) { if ((frm_stat & DVP_CHANNEL1_FRM_READ) == DVP_CHANNEL1_FRM_READ) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID1\n"); *intstat &= ~DVP_FRAME_END_ID1; return RKCIF_STREAM_MIPI_ID1; } if (*intstat & DVP_FRAME_END_ID2) { if ((frm_stat & DVP_CHANNEL2_FRM_READ) == DVP_CHANNEL2_FRM_READ) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID2\n"); *intstat &= ~DVP_FRAME_END_ID2; return RKCIF_STREAM_MIPI_ID2; } if (*intstat & DVP_FRAME_END_ID3) { if ((frm_stat & DVP_CHANNEL3_FRM_READ) == DVP_CHANNEL3_FRM_READ) v4l2_warn(v4l2_dev, "frame0/1 trigger simultaneously in DVP ID3\n"); *intstat &= ~DVP_FRAME_END_ID3; 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, struct rkcif_device, reset_watchdog_timer); struct rkcif_stream *stream = &dev->stream[RKCIF_STREAM_MIPI_ID0]; struct rkcif_hw_timer *hw_timer = &dev->hw_dev->hw_timer; bool is_triggered = false, is_assign_triggered = false, is_first_err = false; unsigned long flags; u64 cur_time, diff_time; spin_lock_irqsave(&timer->csi2_err_lock, flags); if (timer->csi2_err_cnt_even != 0 && timer->csi2_err_cnt_odd != 0) { timer->csi2_err_cnt_odd = 0; timer->csi2_err_cnt_even = 0; hw_timer->reset_src = RKCIF_RESET_SRC_ERR_CSI2; timer->csi2_err_triggered_cnt++; if (timer->csi2_err_triggered_cnt == 1) { is_first_err = true; timer->csi2_first_err_timestamp = ktime_get_ns(); } is_assign_triggered = true; v4l2_info(&dev->v4l2_dev, "find csi2 err cnt is:%d\n", timer->csi2_err_triggered_cnt); } if (!is_first_err) { if (timer->csi2_err_triggered_cnt >= 1) { cur_time = ktime_get_ns(); diff_time = cur_time - timer->csi2_first_err_timestamp; diff_time = div_u64(diff_time, 1000000); if (diff_time >= hw_timer->err_time_interval) { is_triggered = true; v4l2_info(&dev->v4l2_dev, "trigger reset for time out of csi err\n"); goto end_judge; } if (!is_assign_triggered && (timer->csi2_err_cnt_odd == 0 || timer->csi2_err_cnt_even == 0)) { is_triggered = true; v4l2_info(&dev->v4l2_dev, "trigger reset for csi err\n"); goto end_judge; } } } /* * when fs cnt is beyond 2, it indicates that frame end is not coming, * or fs and fe had been not paired. */ if (hw_timer->monitor_mode != RKCIF_MONITOR_MODE_HOTPLUG && (stream->is_fs_fe_not_paired || stream->fs_cnt_in_single_frame > RKCIF_FS_DETECTED_NUM)) { is_triggered = true; v4l2_info(&dev->v4l2_dev, "reset for fs & fe not paired\n"); } if (dev->irq_stats.csi_bwidth_lack_cnt) { is_triggered = true; dev->irq_stats.csi_bwidth_lack_cnt = 0; v4l2_info(&dev->v4l2_dev, "reset for bandwidth lack\n"); } if (dev->irq_stats.csi_overflow_cnt) { is_triggered = true; dev->irq_stats.csi_overflow_cnt = 0; } end_judge: spin_unlock_irqrestore(&timer->csi2_err_lock, flags); return is_triggered; } static s32 rkcif_get_sensor_vblank(struct rkcif_device *dev) { struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; struct v4l2_subdev *sd = terminal_sensor->sd; struct v4l2_ctrl_handler *hdl = sd->ctrl_handler; struct v4l2_ctrl *ctrl = NULL; if (!list_empty(&hdl->ctrls)) { list_for_each_entry(ctrl, &hdl->ctrls, node) { if (ctrl->id == V4L2_CID_VBLANK) return ctrl->val; } } return 0; } static s32 rkcif_get_sensor_vblank_def(struct rkcif_device *dev) { struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; struct v4l2_subdev *sd = terminal_sensor->sd; struct v4l2_ctrl_handler *hdl = sd->ctrl_handler; struct v4l2_ctrl *ctrl = NULL; if (!list_empty(&hdl->ctrls)) { list_for_each_entry(ctrl, &hdl->ctrls, node) { if (ctrl->id == V4L2_CID_VBLANK) return ctrl->default_value; } } return 0; } static void rkcif_cal_csi_crop_width_vwidth(struct rkcif_stream *stream, u32 raw_width, u32 *crop_width, u32 *crop_vwidth) { struct rkcif_device *dev = stream->cifdev; struct csi_channel_info *channel = &dev->channels[stream->id]; const struct cif_output_fmt *fmt; u32 fourcc; 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; } *crop_width = raw_width; /* * for mipi or lvds, when enable compact, the virtual width of raw10/raw12 * needs aligned with :ALIGN(bits_per_pixel * width / 8, 8), if enable 16bit mode * needs aligned with :ALIGN(bits_per_pixel * width * 2, 8), to optimize reading and * writing of ddr, aliged with 256 */ if (fmt->fmt_type == CIF_FMT_TYPE_RAW && stream->is_compact && fmt->csi_fmt_val != CSI_WRDDR_TYPE_RGB888) { *crop_vwidth = ALIGN(raw_width * fmt->raw_bpp / 8, 256); } else { *crop_vwidth = ALIGN(raw_width * fmt->bpp[0] / 8, 8); } if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888) *crop_width = raw_width * fmt->bpp[0] / 8; /* * rk cif don't support output yuyv fmt data * if user request yuyv fmt, the input mode must be RAW8 * and the width is double Because the real input fmt is * yuyv */ fourcc = stream->cif_fmt_out->fourcc; if (fourcc == V4L2_PIX_FMT_YUYV || fourcc == V4L2_PIX_FMT_YVYU || fourcc == V4L2_PIX_FMT_UYVY || fourcc == V4L2_PIX_FMT_VYUY) { *crop_width = 2 * raw_width; *crop_vwidth *= 2; } } static void rkcif_dynamic_crop(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; struct v4l2_mbus_config *mbus; const struct cif_output_fmt *fmt; u32 raw_width, crop_width = 64, crop_vwidth = 64, crop_height = 64, crop_x = 0, crop_y = 0; if (!cif_dev->active_sensor) return; mbus = &cif_dev->active_sensor->mbus; if (mbus->type == V4L2_MBUS_CSI2 || mbus->type == V4L2_MBUS_CCP2) { struct csi_channel_info *channel = &cif_dev->channels[stream->id]; if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888) crop_x = 3 * stream->crop[CROP_SRC_ACT].left; else crop_x = stream->crop[CROP_SRC_ACT].left; crop_y = stream->crop[CROP_SRC_ACT].top; raw_width = stream->crop[CROP_SRC_ACT].width; crop_height = stream->crop[CROP_SRC_ACT].height; rkcif_cal_csi_crop_width_vwidth(stream, raw_width, &crop_width, &crop_vwidth); rkcif_write_register(cif_dev, get_reg_index_of_id_crop_start(channel->id), crop_y << 16 | crop_x); rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl1(channel->id), crop_height << 16 | crop_width); rkcif_write_register(cif_dev, get_reg_index_of_frm0_y_vlw(channel->id), crop_vwidth); rkcif_write_register(cif_dev, get_reg_index_of_frm1_y_vlw(channel->id), crop_vwidth); rkcif_write_register(cif_dev, get_reg_index_of_frm0_uv_vlw(channel->id), crop_vwidth); rkcif_write_register(cif_dev, get_reg_index_of_frm1_uv_vlw(channel->id), crop_vwidth); } else { raw_width = stream->crop[CROP_SRC_ACT].width; crop_width = raw_width; crop_vwidth = raw_width; crop_height = stream->crop[CROP_SRC_ACT].height; crop_x = stream->crop[CROP_SRC_ACT].left; crop_y = stream->crop[CROP_SRC_ACT].top; rkcif_write_register(cif_dev, CIF_REG_DVP_CROP, crop_y << CIF_CROP_Y_SHIFT | crop_x); if (stream->cif_fmt_in->fmt_type == CIF_FMT_TYPE_RAW) { fmt = find_output_fmt(stream, stream->pixm.pixelformat); if (fmt->fourcc == V4L2_PIX_FMT_GREY || fmt->fourcc == V4L2_PIX_FMT_SRGGB8 || fmt->fourcc == V4L2_PIX_FMT_SGRBG8 || fmt->fourcc == V4L2_PIX_FMT_SGBRG8 || fmt->fourcc == V4L2_PIX_FMT_SBGGR8) crop_vwidth = ALIGN(raw_width * fmt->raw_bpp / 8, 256); else crop_vwidth = raw_width * rkcif_cal_raw_vir_line_ratio(stream, fmt); } rkcif_write_register(cif_dev, CIF_REG_DVP_VIR_LINE_WIDTH, crop_vwidth); rkcif_write_register(cif_dev, CIF_REG_DVP_SET_SIZE, crop_height << 16 | crop_width); } stream->crop_dyn_en = false; } void rkcif_monitor_reset_event(struct rkcif_hw *hw) { struct rkcif_hw_timer *hw_timer = &hw->hw_timer; struct rkcif_timer *timer = NULL; struct rkcif_device *cif_dev = NULL; struct rkcif_stream *stream = NULL; unsigned long flags; int i = 0, j = 0; if (hw_timer->is_running) return; if (hw_timer->monitor_mode == RKCIF_MONITOR_MODE_IDLE) return; spin_lock_irqsave(&hw_timer->timer_lock, flags); hw_timer->is_running = true; for (j = 0; i < hw->dev_num; j++) { cif_dev = hw->cif_dev[i]; timer = &cif_dev->reset_watchdog_timer; for (i = 0; i < cif_dev->num_channels; i++) { stream = &cif_dev->stream[i]; if (stream->state == RKCIF_STATE_STREAMING) timer->last_buf_wakeup_cnt[i] = stream->frame_idx; } } hw_timer->cycle_jif = msecs_to_jiffies(hw_timer->monitor_cycle); hw_timer->timer.expires = jiffies + hw_timer->cycle_jif; mod_timer(&hw_timer->timer, hw_timer->timer.expires); spin_unlock_irqrestore(&hw_timer->timer_lock, flags); v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "start monitor timer, monitor cycle %d\n", hw_timer->monitor_cycle); } static void rkcif_rdbk_frame_end(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *sensor = &stream->cifdev->terminal_sensor; u32 denominator, numerator; u64 l_ts, m_ts, s_ts, time = 30000000LL; int ret, fps = -1; if (dev->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) { 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 || dev->stream[RKCIF_STREAM_MIPI_ID2].state != RKCIF_STATE_STREAMING) return; } numerator = sensor->fi.interval.numerator; denominator = sensor->fi.interval.denominator; if (denominator && numerator) time = numerator * 1000 / denominator * 1000 * 1000; if (dev->hdr.mode == HDR_X3) { if (dev->rdbk_buf[RDBK_L] && dev->rdbk_buf[RDBK_M] && dev->rdbk_buf[RDBK_S]) { l_ts = dev->rdbk_buf[RDBK_L]->vb.vb2_buf.timestamp; m_ts = dev->rdbk_buf[RDBK_M]->vb.vb2_buf.timestamp; s_ts = dev->rdbk_buf[RDBK_S]->vb.vb2_buf.timestamp; if (m_ts < l_ts || s_ts < m_ts) { v4l2_err(&dev->v4l2_dev, "s/m/l frame err, timestamp s:%lld m:%lld l:%lld\n", s_ts, m_ts, l_ts); goto RDBK_FRM_UNMATCH; } if ((m_ts - l_ts) > time || (s_ts - m_ts) > time) { ret = v4l2_subdev_call(sensor->sd, video, g_frame_interval, &sensor->fi); if (!ret) { denominator = sensor->fi.interval.denominator; numerator = sensor->fi.interval.numerator; if (denominator && numerator) { time = numerator * 1000 / denominator * 1000 * 1000; fps = denominator / numerator; } } if ((m_ts - l_ts) > time || (s_ts - m_ts) > time) { v4l2_err(&dev->v4l2_dev, "timestamp no match, s:%lld m:%lld l:%lld, fps:%d\n", s_ts, m_ts, l_ts, fps); goto RDBK_FRM_UNMATCH; } } dev->rdbk_buf[RDBK_L]->vb.sequence = dev->rdbk_buf[RDBK_S]->vb.sequence; dev->rdbk_buf[RDBK_M]->vb.sequence = dev->rdbk_buf[RDBK_S]->vb.sequence; rkcif_vb_done_oneframe(&dev->stream[RKCIF_STREAM_MIPI_ID0], &dev->rdbk_buf[RDBK_L]->vb); rkcif_vb_done_oneframe(&dev->stream[RKCIF_STREAM_MIPI_ID1], &dev->rdbk_buf[RDBK_M]->vb); rkcif_vb_done_oneframe(&dev->stream[RKCIF_STREAM_MIPI_ID2], &dev->rdbk_buf[RDBK_S]->vb); } else { if (!dev->rdbk_buf[RDBK_L]) v4l2_err(&dev->v4l2_dev, "lost long frames\n"); if (!dev->rdbk_buf[RDBK_M]) v4l2_err(&dev->v4l2_dev, "lost medium frames\n"); if (!dev->rdbk_buf[RDBK_S]) v4l2_err(&dev->v4l2_dev, "lost short frames\n"); goto RDBK_FRM_UNMATCH; } } else if (dev->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; if (s_ts < l_ts) { v4l2_err(&dev->v4l2_dev, "s/l frame err, timestamp s:%lld l:%lld\n", s_ts, l_ts); goto RDBK_FRM_UNMATCH; } if ((s_ts - l_ts) > time) { ret = v4l2_subdev_call(sensor->sd, video, g_frame_interval, &sensor->fi); if (!ret) { denominator = sensor->fi.interval.denominator; numerator = sensor->fi.interval.numerator; if (denominator && numerator) { time = numerator * 1000 / denominator * 1000 * 1000; fps = denominator / numerator; } } if ((s_ts - l_ts) > time) { v4l2_err(&dev->v4l2_dev, "timestamp no match, s:%lld l:%lld, fps:%d\n", s_ts, l_ts, fps); goto RDBK_FRM_UNMATCH; } } dev->rdbk_buf[RDBK_L]->vb.sequence = dev->rdbk_buf[RDBK_M]->vb.sequence; rkcif_vb_done_oneframe(&dev->stream[RKCIF_STREAM_MIPI_ID0], &dev->rdbk_buf[RDBK_L]->vb); rkcif_vb_done_oneframe(&dev->stream[RKCIF_STREAM_MIPI_ID1], &dev->rdbk_buf[RDBK_M]->vb); } else { if (!dev->rdbk_buf[RDBK_L]) v4l2_err(&dev->v4l2_dev, "lost long frames\n"); if (!dev->rdbk_buf[RDBK_M]) v4l2_err(&dev->v4l2_dev, "lost short frames\n"); goto RDBK_FRM_UNMATCH; } } else { rkcif_vb_done_oneframe(stream, &dev->rdbk_buf[RDBK_S]->vb); } dev->rdbk_buf[RDBK_L] = NULL; dev->rdbk_buf[RDBK_M] = NULL; dev->rdbk_buf[RDBK_S] = NULL; return; RDBK_FRM_UNMATCH: if (dev->rdbk_buf[RDBK_L]) { dev->rdbk_buf[RDBK_L]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; rkcif_buf_queue(&dev->rdbk_buf[RDBK_L]->vb.vb2_buf); } if (dev->rdbk_buf[RDBK_M]) { dev->rdbk_buf[RDBK_M]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; rkcif_buf_queue(&dev->rdbk_buf[RDBK_M]->vb.vb2_buf); } if (dev->rdbk_buf[RDBK_S]) { dev->rdbk_buf[RDBK_S]->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; rkcif_buf_queue(&dev->rdbk_buf[RDBK_S]->vb.vb2_buf); } dev->rdbk_buf[RDBK_L] = NULL; dev->rdbk_buf[RDBK_M] = NULL; dev->rdbk_buf[RDBK_S] = NULL; } static void rkcif_buf_done_prepare(struct rkcif_stream *stream, struct rkcif_buffer *active_buf, int mipi_id, u32 mode) { unsigned long flags; struct vb2_v4l2_buffer *vb_done = NULL; struct rkcif_device *cif_dev = stream->cifdev; if (active_buf) { vb_done = &active_buf->vb; vb_done->vb2_buf.timestamp = ktime_get_ns(); vb_done->sequence = stream->frame_idx; if (stream->is_line_wake_up) { spin_lock_irqsave(&stream->fps_lock, flags); if (mode) stream->fps_stats.frm0_timestamp = vb_done->vb2_buf.timestamp; else stream->fps_stats.frm1_timestamp = vb_done->vb2_buf.timestamp; stream->readout.wk_timestamp = vb_done->vb2_buf.timestamp; spin_unlock_irqrestore(&stream->fps_lock, flags); } if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) vb_done->sequence /= 2; } if (cif_dev->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); } else { if (active_buf) rkcif_vb_done_oneframe(stream, vb_done); } } else { if (cif_dev->is_start_hdr) { spin_lock_irqsave(&cif_dev->hdr_lock, flags); if (mipi_id == RKCIF_STREAM_MIPI_ID0) { 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", 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; rkcif_buf_queue(&cif_dev->rdbk_buf[RDBK_L]->vb.vb2_buf); } if (active_buf) cif_dev->rdbk_buf[RDBK_L] = active_buf; } else if (mipi_id == RKCIF_STREAM_MIPI_ID1) { 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", 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; rkcif_buf_queue(&cif_dev->rdbk_buf[RDBK_M]->vb.vb2_buf); } if (active_buf) cif_dev->rdbk_buf[RDBK_M] = active_buf; if (cif_dev->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", 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; rkcif_buf_queue(&cif_dev->rdbk_buf[RDBK_S]->vb.vb2_buf); } if (active_buf) cif_dev->rdbk_buf[RDBK_S] = active_buf; if (cif_dev->hdr.mode == HDR_X3) rkcif_rdbk_frame_end(stream); } spin_unlock_irqrestore(&cif_dev->hdr_lock, flags); } else { if (active_buf) { vb_done->vb2_buf.state = VB2_BUF_STATE_ACTIVE; rkcif_buf_queue(&vb_done->vb2_buf); } v4l2_info(&cif_dev->v4l2_dev, "warning:hdr runs stream[%d], stream[0]:%s stream[1]:%s stream[2]:%s stream[3]:%s\n", stream->id, cif_dev->stream[0].state != RKCIF_STATE_STREAMING ? "stopped" : "running", cif_dev->stream[1].state != RKCIF_STATE_STREAMING ? "stopped" : "running", cif_dev->stream[2].state != RKCIF_STATE_STREAMING ? "stopped" : "running", cif_dev->stream[3].state != RKCIF_STATE_STREAMING ? "stopped" : "running"); } } } static void rkcif_line_wake_up(struct rkcif_stream *stream, int mipi_id) { u32 mode; struct rkcif_buffer *active_buf = NULL; int ret = 0; mode = stream->line_int_cnt % 2; if (mode) { if (stream->curr_buf) active_buf = stream->curr_buf; } else { if (stream->next_buf) active_buf = stream->next_buf; } if (stream->stopping) { stream->is_can_stop = true; return; } ret = rkcif_get_new_buffer_wake_up_mode(stream); if (ret) goto end_wake_up; rkcif_buf_done_prepare(stream, active_buf, mipi_id, mode); end_wake_up: stream->frame_idx++; } static void rkcif_deal_readout_time(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; struct rkcif_stream *detect_stream = &cif_dev->stream[0]; unsigned long flags; spin_lock_irqsave(&stream->fps_lock, flags); stream->readout.fe_timestamp = ktime_get_ns(); 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)) { 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)) { 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)) { 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; } if (!stream->is_line_wake_up) detect_stream->readout.early_time = 0; spin_unlock_irqrestore(&stream->fps_lock, flags); } static void rkcif_update_stream(struct rkcif_device *cif_dev, struct rkcif_stream *stream, int mipi_id) { struct rkcif_buffer *active_buf = NULL; unsigned long flags; int ret = 0; 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; } if (!stream->is_line_wake_up) { spin_lock_irqsave(&stream->fps_lock, flags); if (stream->frame_phase & CIF_CSI_FRAME0_READY) { if (stream->curr_buf) active_buf = stream->curr_buf; stream->fps_stats.frm0_timestamp = ktime_get_ns(); } else if (stream->frame_phase & CIF_CSI_FRAME1_READY) { if (stream->next_buf) active_buf = stream->next_buf; stream->fps_stats.frm1_timestamp = ktime_get_ns(); } spin_unlock_irqrestore(&stream->fps_lock, flags); } if (cif_dev->inf_id == RKCIF_MIPI_LVDS) rkcif_deal_readout_time(stream); if (cif_dev->chip_id == CHIP_RV1126_CIF || cif_dev->chip_id == CHIP_RV1126_CIF_LITE || cif_dev->chip_id == CHIP_RK3568_CIF) rkcif_luma_isr(&cif_dev->luma_vdev, mipi_id, stream->frame_idx); if (!stream->is_line_wake_up) { ret = rkcif_assign_new_buffer_pingpong(stream, RKCIF_YUV_ADDR_STATE_UPDATE, mipi_id); if (ret) goto end; } else { ret = rkcif_update_new_buffer_wake_up_mode(stream); if (ret) return; } if (!stream->is_line_wake_up) rkcif_buf_done_prepare(stream, active_buf, mipi_id, 0); end: if (!stream->is_line_wake_up) 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) val = rkcif_csi2_get_sof(); else if (sensor->mbus.type == V4L2_MBUS_CCP2) val = rkcif_lvds_get_sof(cif_dev); else if (sensor->mbus.type == V4L2_MBUS_PARALLEL || sensor->mbus.type == V4L2_MBUS_BT656) val = rkcif_dvp_get_sof(cif_dev); return val; } static void rkcif_set_sof(struct rkcif_device *cif_dev, u32 seq) { struct rkcif_sensor_info *sensor = cif_dev->active_sensor; if (sensor->mbus.type == V4L2_MBUS_CSI2) rkcif_csi2_set_sof(seq); else if (sensor->mbus.type == V4L2_MBUS_CCP2) rkcif_lvds_set_sof(cif_dev, seq); else if (sensor->mbus.type == V4L2_MBUS_PARALLEL || sensor->mbus.type == V4L2_MBUS_BT656) rkcif_dvp_set_sof(cif_dev, seq); } static int rkcif_streamoff_in_reset(struct rkcif_device *cif_dev, struct rkcif_stream *resume_stream[], struct rkcif_resume_info *resume_info, enum rkmodule_reset_src reset_src) { struct rkcif_stream *stream = NULL; struct rkcif_pipeline *p = &cif_dev->pipe; struct rkcif_sensor_info *terminal_sensor = &cif_dev->terminal_sensor; u32 on, sof_cnt; int i, j, ret = 0; int stream_off_cnt = 0; for (i = 0, j = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { stream = &cif_dev->stream[i]; if (stream->state == RKCIF_STATE_STREAMING) { stream_off_cnt++; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "stream[%d] stopping\n", stream->id); rkcif_stream_stop(stream); atomic_dec(&p->stream_cnt); if (stream->id == RKCIF_STREAM_MIPI_ID0) { sof_cnt = rkcif_get_sof(cif_dev); v4l2_err(&cif_dev->v4l2_dev, "%s: stream[%d] sync frmid & csi_sof, frm_id:%d, csi_sof:%d\n", __func__, stream->id, stream->frame_idx, sof_cnt); resume_info->frm_sync_seq = stream->frame_idx; } stream->state = RKCIF_STATE_RESET_IN_STREAMING; stream->is_fs_fe_not_paired = false; stream->fs_cnt_in_single_frame = 0; resume_stream[j] = stream; j += 1; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "%s stop stream[%d] in streaming, frm_id:%d, csi_sof:%d\n", __func__, stream->id, stream->frame_idx, rkcif_csi2_get_sof()); } } if (!stream_off_cnt) return 0; on = 0; for (i = 0; i < p->num_subdevs; i++) { if (p->subdevs[i] == terminal_sensor->sd) { if (reset_src == RKCIF_RESET_SRC_ERR_CSI2 || reset_src == RKCIF_RESET_SRC_ERR_HOTPLUG || reset_src == RKICF_RESET_SRC_ERR_CUTOFF) { ret = v4l2_subdev_call(p->subdevs[i], core, ioctl, RKMODULE_SET_QUICK_STREAM, &on); if (ret) v4l2_err(&cif_dev->v4l2_dev, "quick stream off subdev:%s failed\n", p->subdevs[i]->name); } } if (ret) v4l2_err(&cif_dev->v4l2_dev, "%s:stream %s subdev:%s failed\n", __func__, on ? "on" : "off", p->subdevs[i]->name); } return ret; } static int rkcif_streamon_in_reset(struct rkcif_device *cif_dev, struct rkcif_stream *resume_stream[], struct rkcif_resume_info *resume_info, enum rkmodule_reset_src reset_src) { struct rkcif_timer *timer = &cif_dev->reset_watchdog_timer; struct rkcif_stream *stream = NULL; struct rkcif_pipeline *p = &cif_dev->pipe; struct rkcif_sensor_info *terminal_sensor = &cif_dev->terminal_sensor; int i = 0; int ret = 0; int on = 0; int stream_on_cnt = 0; for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { stream = resume_stream[i]; if (stream == NULL || stream->state != RKCIF_STATE_RESET_IN_STREAMING) break; stream_on_cnt++; stream->fs_cnt_in_single_frame = 0; if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) { if (stream->curr_buf == stream->next_buf) { if (stream->curr_buf) list_add_tail(&stream->curr_buf->queue, &stream->buf_head); } else { if (stream->curr_buf) list_add_tail(&stream->curr_buf->queue, &stream->buf_head); if (stream->next_buf) list_add_tail(&stream->next_buf->queue, &stream->buf_head); } stream->curr_buf = NULL; stream->next_buf = NULL; } if (cif_dev->active_sensor->mbus.type == V4L2_MBUS_CSI2 || cif_dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) ret = rkcif_csi_stream_start(stream); else ret = rkcif_stream_start(stream); if (ret) { v4l2_err(&cif_dev->v4l2_dev, "%s:resume stream[%d] failed\n", __func__, stream->id); goto unlock_stream; } atomic_inc(&p->stream_cnt); stream->streamon_timestamp = ktime_get_ns(); v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "resume stream[%d], frm_idx:%d, csi_sof:%d\n", stream->id, stream->frame_idx, rkcif_get_sof(cif_dev)); } if (!stream_on_cnt) return 0; on = 1; for (i = 0; i < p->num_subdevs; i++) { if (p->subdevs[i] == terminal_sensor->sd) { rkcif_set_sof(cif_dev, resume_info->frm_sync_seq); if (reset_src == RKCIF_RESET_SRC_ERR_CSI2 || reset_src == RKCIF_RESET_SRC_ERR_HOTPLUG || reset_src == RKICF_RESET_SRC_ERR_CUTOFF) { ret = v4l2_subdev_call(p->subdevs[i], core, ioctl, RKMODULE_SET_QUICK_STREAM, &on); if (ret) v4l2_err(&cif_dev->v4l2_dev, "quick stream on subdev:%s failed\n", p->subdevs[i]->name); } } if (ret) v4l2_err(&cif_dev->v4l2_dev, "reset subdev:%s failed\n", p->subdevs[i]->name); } rkcif_start_luma(&cif_dev->luma_vdev, resume_stream[0]->cif_fmt_in); timer->csi2_err_triggered_cnt = 0; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "do rkcif reset successfully!\n"); return 0; unlock_stream: return ret; } static int rkcif_do_reset_work(struct rkcif_device *cif_dev, enum rkmodule_reset_src reset_src) { struct rkcif_stream *resume_stream[RKCIF_MAX_DEV][RKCIF_MAX_STREAM_MIPI] = {0}; struct rkcif_resume_info *resume_info[RKCIF_MAX_DEV]; struct rkcif_hw *hw = cif_dev->hw_dev; struct rkcif_device *cifdev = NULL; int i = 0; int ret = 0; v4l2_info(&cif_dev->v4l2_dev, "do rkcif reset\n"); mutex_lock(&cif_dev->hw_dev->dev_lock); if (hw->reset_work_cancel) { mutex_unlock(&hw->dev_lock); return 0; } for (i = 0; i < hw->dev_num; i++) { cifdev = hw->cif_dev[i]; resume_info[i] = &cifdev->reset_work.resume_info; ret |= rkcif_streamoff_in_reset(cifdev, resume_stream[i], resume_info[i], reset_src); } rockchip_clear_system_status(SYS_STATUS_CIF0); rkcif_do_cru_reset(cif_dev); udelay(30); for (i = 0; i < hw->dev_num; i++) { cifdev = hw->cif_dev[i]; resume_info[i] = &cifdev->reset_work.resume_info; ret |= rkcif_streamon_in_reset(cifdev, resume_stream[i], resume_info[i], reset_src); } rockchip_set_system_status(SYS_STATUS_CIF0); hw->hw_timer.is_running = false; rkcif_monitor_reset_event(cif_dev->hw_dev); v4l2_info(&cif_dev->v4l2_dev, "do rkcif reset successfully!\n"); mutex_unlock(&hw->dev_lock); return ret; } void rkcif_reset_work(struct work_struct *work) { struct rkcif_work_struct *reset_work = container_of(work, struct rkcif_work_struct, work); struct rkcif_device *dev = container_of(reset_work, struct rkcif_device, reset_work); int ret; ret = rkcif_do_reset_work(dev, reset_work->reset_src); if (ret) v4l2_info(&dev->v4l2_dev, "do reset work failed!\n"); } static bool rkcif_is_stream_stop_output(struct rkcif_stream *stream) { struct rkcif_device *dev = stream->cifdev; struct rkcif_hw_timer *hw_timer = &dev->hw_dev->hw_timer; struct rkcif_stream *tmp_stream = NULL; struct v4l2_rect *raw_rect = &dev->terminal_sensor.raw_rect; u64 fps, diff_time; unsigned int i; s64 vblank_def = 0; s32 vblank = 0; u64 numerator = 0; u64 denominator = 0; u64 cur_timestamp = 0; if (dev->hdr.mode == HDR_X3) { for (i = 0; i < 3; i++) { tmp_stream = &dev->stream[i]; if (tmp_stream->state != RKCIF_STATE_STREAMING) return false; } } else if (dev->hdr.mode == HDR_X2) { for (i = 0; i < 2; i++) { tmp_stream = &dev->stream[i]; if (tmp_stream->state != RKCIF_STATE_STREAMING) return false; } } vblank_def = rkcif_get_sensor_vblank_def(dev); vblank = rkcif_get_sensor_vblank(dev); numerator = dev->terminal_sensor.fi.interval.numerator; denominator = dev->terminal_sensor.fi.interval.denominator; fps = div_u64(1000 * numerator, denominator); fps *= (raw_rect->height + vblank); fps = div_u64((raw_rect->height + vblank_def), fps); if (fps > hw_timer->monitor_cycle) { v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "camera work in long expsure, not to check stream loss\n"); return false; } cur_timestamp = ktime_get_ns(); diff_time = cur_timestamp - stream->streamon_timestamp; diff_time = div_u64(diff_time, 1000000); if (diff_time > hw_timer->monitor_cycle + 10) return true; return false; } static void rkcif_init_reset_work(struct rkcif_hw_timer *hw_timer) { struct rkcif_hw *hw = container_of(hw_timer, struct rkcif_hw, hw_timer); struct rkcif_device *dev = NULL; struct rkcif_stream *stream = NULL; struct rkcif_timer *timer; unsigned long flags; int i = 0, j = 0; v4l2_info(&dev->v4l2_dev, "do reset work schedule, run_cnt:%d, reset source:%d\n", hw_timer->run_cnt, hw_timer->reset_src); for (j = 0; j < hw->dev_num; j++) { dev = hw->cif_dev[j]; timer = &dev->reset_watchdog_timer; spin_lock_irqsave(&timer->csi2_err_lock, flags); timer->is_triggered = false; timer->csi2_err_cnt_odd = 0; timer->csi2_err_cnt_even = 0; timer->csi2_err_fs_fe_cnt = 0; timer->notifer_called_cnt = 0; for (i = 0; i < dev->num_channels; i++) { stream = &dev->stream[i]; if (stream->state == RKCIF_STATE_STREAMING) timer->last_buf_wakeup_cnt[stream->id] = stream->frame_idx; } spin_unlock_irqrestore(&timer->csi2_err_lock, flags); } if (hw_timer->is_reset_by_user) { spin_lock_irqsave(&hw->spin_lock, flags); hw->reset_info.is_need_reset = 1; hw->reset_info.reset_src = hw_timer->reset_src; spin_unlock_irqrestore(&hw->spin_lock, flags); } else { dev->reset_work.reset_src = hw_timer->reset_src; if (schedule_work(&dev->reset_work.work)) { v4l2_info(&dev->v4l2_dev, "schedule reset work successfully\n"); } else { v4l2_info(&dev->v4l2_dev, "schedule reset work failed\n"); } } } static int rkcif_detect_reset_event(struct rkcif_stream *stream, struct rkcif_timer *timer, int *check_cnt, bool *is_mod_timer) { struct rkcif_device *dev = stream->cifdev; struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor; struct rkcif_hw_timer *hw_timer = &dev->hw_dev->hw_timer; int ret, is_reset = 0; struct rkmodule_vicap_reset_info rst_info; v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "info: frame end still update(%d, %d), detect cnt: %d, mode:%d check_cnt:%d\n", timer->last_buf_wakeup_cnt[stream->id], stream->frame_idx, hw_timer->run_cnt, hw_timer->monitor_mode, *check_cnt); if (timer->last_buf_wakeup_cnt[stream->id] < stream->frame_idx) { *check_cnt += 1; v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "info: frame end still update(%d, %d), detect cnt: %d, mode:%d\n", timer->last_buf_wakeup_cnt[stream->id], stream->frame_idx, hw_timer->run_cnt, hw_timer->monitor_mode); timer->last_buf_wakeup_cnt[stream->id] = stream->frame_idx; if (hw_timer->monitor_mode == RKCIF_MONITOR_MODE_HOTPLUG) { ret = v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_GET_VICAP_RST_INFO, &rst_info); if (ret) is_reset = 0; else is_reset = rst_info.is_reset; rst_info.is_reset = 0; hw_timer->reset_src = RKCIF_RESET_SRC_ERR_HOTPLUG; v4l2_subdev_call(terminal_sensor->sd, core, ioctl, RKMODULE_SET_VICAP_RST_INFO, &rst_info); if (!is_reset && stream->cifdev->inf_id == RKCIF_MIPI_LVDS) { hw_timer->reset_src = RKICF_RESET_SRC_ERR_CUTOFF; is_reset = rkcif_is_csi2_err_trigger_reset(timer); } } else if (hw_timer->monitor_mode == RKCIF_MONITOR_MODE_CONTINUE) { if (stream->cifdev->inf_id == RKCIF_MIPI_LVDS) is_reset = rkcif_is_csi2_err_trigger_reset(timer); } else if (hw_timer->monitor_mode == RKCIF_MONITOR_MODE_TRIGGER) { if (stream->cifdev->inf_id == RKCIF_MIPI_LVDS) { is_reset = timer->is_csi2_err_occurred; if (is_reset) hw_timer->reset_src = RKCIF_RESET_SRC_ERR_CSI2; timer->is_csi2_err_occurred = false; } } if (is_reset) { rkcif_init_reset_work(hw_timer); return is_reset; } *is_mod_timer = true; } else if (timer->last_buf_wakeup_cnt[stream->id] == stream->frame_idx) { bool is_loss_stream = false; *check_cnt += 1; if (hw_timer->run_cnt > 4) is_loss_stream = rkcif_is_stream_stop_output(stream); if (is_loss_stream) { v4l2_info(&dev->v4l2_dev, "do reset work due to frame end is stopped, run_cnt:%d\n", hw_timer->run_cnt); hw_timer->reset_src = RKICF_RESET_SRC_ERR_CUTOFF; is_reset = true; rkcif_init_reset_work(hw_timer); } else { *is_mod_timer = true; v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "%s fps is reduced\n", __func__); } } return is_reset; } static int rkcif_dvp_err_trigger_reset(struct rkcif_device *dev) { int is_reset = 0; if (dev->irq_stats.dvp_overflow_cnt) { dev->irq_stats.dvp_overflow_cnt = 0; is_reset = 1; } return is_reset; } static int rkcif_detect_reset_event_all_dev(struct rkcif_device *dev, int *check_cnt, bool *is_mod_timer) { struct rkcif_timer *timer = &dev->reset_watchdog_timer; struct rkcif_stream *stream = NULL; int i = 0; int is_reset = 0; int check_cnt_local = 0; for (i = 0; i < dev->num_channels; i++) { stream = &dev->stream[i]; if (stream->state == RKCIF_STATE_STREAMING) { is_reset = rkcif_detect_reset_event(stream, timer, &check_cnt_local, is_mod_timer); if (is_reset) break; } } if (!is_reset && dev->inf_id == RKCIF_DVP) is_reset = rkcif_dvp_err_trigger_reset(dev); if (check_cnt_local) *check_cnt += 1; return is_reset; } void rkcif_reset_watchdog_timer_handler(struct timer_list *t) { struct rkcif_hw_timer *hw_timer = container_of(t, struct rkcif_hw_timer, timer); struct rkcif_hw *hw = container_of(hw_timer, struct rkcif_hw, hw_timer); struct rkcif_device *dev = NULL; unsigned long flags; unsigned int i; int is_reset = 0; int check_cnt = 0; bool is_mod_timer = false; struct rkmodule_vicap_reset_info rst_info; for (i = 0; i < hw->dev_num; i++) { dev = hw->cif_dev[i]; is_reset = rkcif_detect_reset_event_all_dev(dev, &check_cnt, &is_mod_timer); if (is_reset) break; } if (hw_timer->monitor_mode == RKCIF_MONITOR_MODE_HOTPLUG && is_reset) { for (i = 0; i < hw->dev_num; i++) { dev = hw->cif_dev[i]; rst_info.is_reset = 0; if (dev->terminal_sensor.sd) v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_SET_VICAP_RST_INFO, &rst_info); } } if (!is_reset && is_mod_timer) mod_timer(&hw_timer->timer, jiffies + hw_timer->cycle_jif); hw_timer->run_cnt += 1; if (!check_cnt && !is_reset) { spin_lock_irqsave(&hw_timer->timer_lock, flags); hw_timer->is_running = false; spin_unlock_irqrestore(&hw_timer->timer_lock, flags); dev_info(hw->dev, "all stream is stopped, stop reset detect!\n"); } } int rkcif_reset_notifier(struct notifier_block *nb, unsigned long action, void *data) { struct rkcif_device *dev = container_of(nb, struct rkcif_device, reset_notifier); struct rkcif_timer *timer = &dev->reset_watchdog_timer; struct rkcif_hw_timer *hw_timer = &dev->hw_dev->hw_timer; unsigned long flags, val; if (hw_timer->is_running) { val = action & CSI2_ERR_COUNT_ALL_MASK; spin_lock_irqsave(&timer->csi2_err_lock, flags); if ((val % hw_timer->err_ref_cnt) == 0) { timer->notifer_called_cnt++; if ((timer->notifer_called_cnt % 2) == 0) timer->csi2_err_cnt_even = val; else timer->csi2_err_cnt_odd = val; } timer->csi2_err_fs_fe_cnt = (action & CSI2_ERR_FSFE_MASK) >> 8; spin_unlock_irqrestore(&timer->csi2_err_lock, flags); } return 0; } static void rkcif_modify_line_int(struct rkcif_stream *stream, bool en) { struct rkcif_device *cif_dev = stream->cifdev; if (en) { if (cif_dev->wait_line_bak != cif_dev->wait_line) { cif_dev->wait_line_bak = cif_dev->wait_line; rkcif_write_register(cif_dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1, cif_dev->wait_line << 16 | cif_dev->wait_line); rkcif_write_register(cif_dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3, cif_dev->wait_line << 16 | cif_dev->wait_line); } rkcif_write_register_or(cif_dev, CIF_REG_MIPI_LVDS_INTEN, CSI_LINE_INTEN(stream->id)); } else { rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN, ~(CSI_LINE_INTEN(stream->id))); } } static void rkcif_detect_wake_up_mode_change(struct rkcif_stream *stream) { struct rkcif_device *cif_dev = stream->cifdev; if (cif_dev->wait_line && (!stream->is_line_wake_up)) { stream->is_line_wake_up = true; if (stream->frame_phase == CIF_CSI_FRAME0_READY) stream->line_int_cnt = 1; else if (stream->frame_phase == CIF_CSI_FRAME1_READY) stream->line_int_cnt = 0; } else if ((cif_dev->wait_line == 0) && stream->is_line_wake_up) { stream->is_line_wake_up = false; } if (stream->is_line_wake_up) { rkcif_modify_line_int(stream, true); stream->is_line_inten = true; } if (cif_dev->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) { 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) { 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 pixelformat; switch (pixelcode) { case MEDIA_BUS_FMT_Y8_1X8: pixelformat = V4L2_PIX_FMT_GREY; break; case MEDIA_BUS_FMT_SBGGR8_1X8: pixelformat = V4L2_PIX_FMT_SBGGR8; break; case MEDIA_BUS_FMT_SGBRG8_1X8: pixelformat = V4L2_PIX_FMT_SGBRG8; break; case MEDIA_BUS_FMT_SGRBG8_1X8: pixelformat = V4L2_PIX_FMT_SGRBG8; break; case MEDIA_BUS_FMT_SRGGB8_1X8: pixelformat = V4L2_PIX_FMT_SRGGB8; break; case MEDIA_BUS_FMT_Y10_1X10: pixelformat = V4L2_PIX_FMT_Y10; break; case MEDIA_BUS_FMT_SBGGR10_1X10: pixelformat = V4L2_PIX_FMT_SBGGR10; break; case MEDIA_BUS_FMT_SGBRG10_1X10: pixelformat = V4L2_PIX_FMT_SGBRG10; break; case MEDIA_BUS_FMT_SGRBG10_1X10: pixelformat = V4L2_PIX_FMT_SGRBG10; break; case MEDIA_BUS_FMT_SRGGB10_1X10: pixelformat = V4L2_PIX_FMT_SRGGB10; break; case MEDIA_BUS_FMT_Y12_1X12: pixelformat = V4L2_PIX_FMT_Y12; break; case MEDIA_BUS_FMT_SBGGR12_1X12: pixelformat = V4L2_PIX_FMT_SBGGR12; break; case MEDIA_BUS_FMT_SGBRG12_1X12: pixelformat = V4L2_PIX_FMT_SGBRG12; break; case MEDIA_BUS_FMT_SGRBG12_1X12: pixelformat = V4L2_PIX_FMT_SGRBG12; break; case MEDIA_BUS_FMT_SRGGB12_1X12: pixelformat = V4L2_PIX_FMT_SRGGB12; break; case MEDIA_BUS_FMT_SPD_2X8: pixelformat = V4l2_PIX_FMT_SPD16; break; case MEDIA_BUS_FMT_EBD_1X8: pixelformat = V4l2_PIX_FMT_EBD8; break; default: pixelformat = V4L2_PIX_FMT_SRGGB10; } return pixelformat; } void rkcif_set_default_fmt(struct rkcif_device *cif_dev) { struct v4l2_subdev_selection input_sel; struct v4l2_pix_format_mplane pixm; struct v4l2_subdev_format fmt; int stream_num = 0; int ret, i; if (cif_dev->chip_id < CHIP_RV1126_CIF) return; stream_num = RKCIF_MAX_STREAM_MIPI; if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { for (i = 0; i < stream_num; i++) { if (i == RKCIF_STREAM_MIPI_ID3) cif_dev->stream[i].is_compact = false; memset(&fmt, 0, sizeof(fmt)); fmt.pad = i; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 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.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; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); if (!ret) { pixm.width = input_sel.r.width; pixm.height = input_sel.r.height; } rkcif_set_fmt(&cif_dev->stream[i], &pixm, false); } } } #define CSI_START_INTSTAT(id) (0x3 << ((id) * 2)) void rkcif_irq_pingpong(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 = 0x0, i = 0xff, bak_intstat = 0x0; unsigned long flags; int ret = 0; if (!cif_dev->active_sensor) return; mbus = &cif_dev->active_sensor->mbus; if ((mbus->type == V4L2_MBUS_CSI2 || mbus->type == V4L2_MBUS_CCP2) && (cif_dev->chip_id == CHIP_RK1808_CIF || cif_dev->chip_id == CHIP_RV1126_CIF || cif_dev->chip_id == CHIP_RK3568_CIF)) { 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 */ rkcif_write_register(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT, intstat); if (intstat & CSI_FIFO_OVERFLOW) { 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) { 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) { 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 | CSI_FRAME1_START_ID0)) == (CSI_FRAME0_START_ID0 | CSI_FRAME1_START_ID0)) { v4l2_err(&cif_dev->v4l2_dev, "%s:ERR: double fs in one fs int\n", __func__); } 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); } } 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->is_can_stop) { 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); rkcif_update_stream(cif_dev, stream, mipi_id); 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_dbg(2, rkcif_debug, &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--; } } cif_dev->irq_stats.all_frm_end_cnt++; } for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { if (intstat & CSI_START_INTSTAT(i)) { stream = &cif_dev->stream[i]; if (i == 0) { if (mbus->type == V4L2_MBUS_CSI2) rkcif_csi2_event_inc_sof(); else if (mbus->type == V4L2_MBUS_CCP2) rkcif_lvds_event_inc_sof(cif_dev); stream->fs_cnt_in_single_frame++; spin_lock_irqsave(&stream->fps_lock, flags); stream->readout.fs_timestamp = ktime_get_ns(); spin_unlock_irqrestore(&stream->fps_lock, flags); } else { spin_lock_irqsave(&stream->fps_lock, flags); stream->readout.fs_timestamp = ktime_get_ns(); spin_unlock_irqrestore(&stream->fps_lock, flags); } } } } else { u32 lastline, lastpix, ctl; u32 cif_frmst, frmid, int_en; struct rkcif_stream *stream; int ch_id; intstat = rkcif_read_register(cif_dev, CIF_REG_DVP_INTSTAT); cif_frmst = rkcif_read_register(cif_dev, CIF_REG_DVP_FRAME_STATUS); lastline = rkcif_read_register(cif_dev, CIF_REG_DVP_LAST_LINE); lastline = CIF_FETCH_Y_LAST_LINE(lastline); lastpix = rkcif_read_register(cif_dev, CIF_REG_DVP_LAST_PIX); lastpix = CIF_FETCH_Y_LAST_LINE(lastpix); ctl = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL); rkcif_write_register(cif_dev, CIF_REG_DVP_INTSTAT, intstat); stream = &cif_dev->stream[RKCIF_STREAM_CIF]; if ((intstat & LINE_INT_END) && !(intstat & FRAME_END) && (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); 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); cif_dev->dvp_sof_in_oneframe = 1; } } } if (intstat & BUS_ERR) { cif_dev->irq_stats.dvp_bus_err_cnt++; v4l2_info(&cif_dev->v4l2_dev, "dvp bus err\n"); } if (intstat & DVP_ALL_OVERFLOW) { cif_dev->irq_stats.dvp_overflow_cnt++; v4l2_info(&cif_dev->v4l2_dev, "dvp overflow err\n"); } if (intstat & LINE_ERR) { cif_dev->irq_stats.dvp_line_err_cnt++; v4l2_info(&cif_dev->v4l2_dev, "dvp line err\n"); } if (intstat & PIX_ERR) { cif_dev->irq_stats.dvp_pix_err_cnt++; v4l2_info(&cif_dev->v4l2_dev, "dvp pix err\n"); } 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); } /* 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)) { stream = &cif_dev->stream[RKCIF_STREAM_CIF]; if (stream->stopping) /* To stop CIF ASAP, before FRAME_END irq */ rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, ctl & (~ENABLE_CAPTURE)); } if (cif_dev->chip_id <= CHIP_RK1808_CIF) { stream = &cif_dev->stream[RKCIF_STREAM_CIF]; if ((intstat & FRAME_END)) { struct vb2_v4l2_buffer *vb_done = NULL; 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); cif_dev->dvp_sof_in_oneframe = 0; if (stream->stopping) { rkcif_stream_stop(stream); stream->stopping = false; rkcif_assign_dummy_buffer(stream); wake_up(&stream->wq_stopped); return; } frmid = CIF_GET_FRAME_ID(cif_frmst); if ((cif_frmst == 0xfffd0002) || (cif_frmst == 0xfffe0002)) { v4l2_info(&cif_dev->v4l2_dev, "frmid:%d, frmstat:0x%x\n", frmid, cif_frmst); rkcif_write_register(cif_dev, CIF_REG_DVP_FRAME_STATUS, cif_frmst & 0x3); } if (lastline != stream->pixm.height || (!(cif_frmst & CIF_F0_READY) && !(cif_frmst & CIF_F1_READY))) { cif_dev->dvp_sof_in_oneframe = 1; v4l2_err(&cif_dev->v4l2_dev, "Bad frame, pp irq:0x%x frmst:0x%x size:%dx%d\n", intstat, cif_frmst, lastpix, lastline); return; } if (cif_frmst & CIF_F0_READY) { if (stream->curr_buf) vb_done = &stream->curr_buf->vb; stream->frame_phase = CIF_CSI_FRAME0_READY; } else if (cif_frmst & CIF_F1_READY) { if (stream->next_buf) vb_done = &stream->next_buf->vb; stream->frame_phase = CIF_CSI_FRAME1_READY; } spin_lock_irqsave(&stream->fps_lock, flags); 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_irqrestore(&stream->fps_lock, flags); ret = rkcif_assign_new_buffer_oneframe(stream, RKCIF_YUV_ADDR_STATE_UPDATE); if (vb_done && (!ret)) { vb_done->sequence = stream->frame_idx; rkcif_vb_done_oneframe(stream, vb_done); } stream->frame_idx++; cif_dev->irq_stats.all_frm_end_cnt++; } } else { for (i = 0; i < RKCIF_MAX_STREAM_DVP; i++) { ch_id = rkcif_dvp_g_ch_id(&cif_dev->v4l2_dev, &intstat, cif_frmst); if (ch_id < 0) continue; if (ch_id == RKCIF_STREAM_MIPI_ID0) { 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); cif_dev->dvp_sof_in_oneframe = 0; } 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 = DVP_FRM_STS_ID0(cif_frmst); break; case RKCIF_STREAM_MIPI_ID1: stream->frame_phase = DVP_FRM_STS_ID1(cif_frmst); break; case RKCIF_STREAM_MIPI_ID2: stream->frame_phase = DVP_FRM_STS_ID2(cif_frmst); break; case RKCIF_STREAM_MIPI_ID3: stream->frame_phase = DVP_FRM_STS_ID3(cif_frmst); break; } frmid = CIF_GET_FRAME_ID(cif_frmst); if ((frmid == 0xfffd) || (frmid == 0xfffe)) { v4l2_info(&cif_dev->v4l2_dev, "frmid:%d, frmstat:0x%x\n", frmid, cif_frmst); rkcif_write_register(cif_dev, CIF_REG_DVP_FRAME_STATUS, cif_frmst & 0xffff); } rkcif_update_stream(cif_dev, stream, ch_id); cif_dev->irq_stats.all_frm_end_cnt++; } } if (stream->crop_dyn_en) rkcif_dynamic_crop(stream); } } void rkcif_irq_lite_lvds(struct rkcif_device *cif_dev) { struct rkcif_stream *stream; struct v4l2_mbus_config *mbus = &cif_dev->active_sensor->mbus; unsigned int intstat, i = 0xff; if (mbus->type == V4L2_MBUS_CCP2 && cif_dev->chip_id == CHIP_RV1126_CIF_LITE) { 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_INT_NUM_ID0_1); /* clear all interrupts that has been triggered */ rkcif_write_register(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT, intstat); if (intstat & CSI_FIFO_OVERFLOW) { v4l2_err(&cif_dev->v4l2_dev, "ERROR: cif lite lvds fifo overflow, intstat:0x%x, lastline:%d!!\n", intstat, lastline); return; } if (intstat & CSI_BANDWIDTH_LACK) { v4l2_err(&cif_dev->v4l2_dev, "ERROR: cif lite lvds bandwidth lack, intstat:0x%x!!\n", intstat); return; } if (intstat & CSI_ALL_ERROR_INTEN) { v4l2_err(&cif_dev->v4l2_dev, "ERROR: cif lite lvds all err:0x%x!!\n", intstat); return; } if (intstat & CSI_FRAME0_START_ID0) rkcif_lvds_event_inc_sof(cif_dev); if (intstat & CSI_FRAME1_START_ID0) rkcif_lvds_event_inc_sof(cif_dev); /* 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) { 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; } rkcif_update_stream(cif_dev, stream, mipi_id); } cif_dev->irq_stats.all_frm_end_cnt++; } }