/* * Samsung Exynos5 SoC series FIMC-IS driver * * exynos5 fimc-is video functions * * Copyright (c) 2011 Samsung Electronics Co., Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "is-time.h" #include "is-core.h" #include "is-param.h" #include "is-cmd.h" #include "is-err.h" #include "is-debug.h" #include "pablo-mem.h" #include "is-video.h" /* * copy from 'include/media/v4l2-ioctl.h' * #define V4L2_DEV_DEBUG_IOCTL 0x01 * #define V4L2_DEV_DEBUG_IOCTL_ARG 0x02 * #define V4L2_DEV_DEBUG_FOP 0x04 * #define V4L2_DEV_DEBUG_STREAMING 0x08 * #define V4L2_DEV_DEBUG_POLL 0x10 */ #define V4L2_DEV_DEBUG_DMA 0x100 #define DDD_CTRL_IMAGE 0x01 #define DDD_CTRL_META 0x02 #define DDD_CTRL_TYPE_MASK #define DDD_TYPE_PERIOD 0 /* all frame count matching period */ #define DDD_TYPE_INTERVAL 1 /* from any frame count to greater one */ #define DDD_TYPE_ONESHOT 2 /* specific frame count only */ static unsigned int dbg_dma_dump_ctrl; static unsigned int dbg_dma_dump_type = DDD_TYPE_PERIOD; static unsigned int dbg_dma_dump_arg1 = 30; /* every frame(s) */ static unsigned int dbg_dma_dump_arg2; /* for interval type */ static int param_get_dbg_dma_dump_ctrl(char *buffer, const struct kernel_param *kp) { int ret; ret = sprintf(buffer, "DMA dump control: "); if (!dbg_dma_dump_ctrl) { ret += sprintf(buffer + ret, "None\n"); ret += sprintf(buffer + ret, "\t- image(0x1)\n"); ret += sprintf(buffer + ret, "\t- meta (0x2)\n"); } else { if (dbg_dma_dump_ctrl & DDD_CTRL_IMAGE) ret += sprintf(buffer + ret, "dump image(0x1) | "); if (dbg_dma_dump_ctrl & DDD_CTRL_META) ret += sprintf(buffer + ret, "dump meta (0x2) | "); ret -= 3; ret += sprintf(buffer + ret, "\n"); } return ret; } static const struct kernel_param_ops param_ops_dbg_dma_dump_ctrl = { .set = param_set_uint, .get = param_get_dbg_dma_dump_ctrl, }; static int param_get_dbg_dma_dump_type(char *buffer, const struct kernel_param *kp) { int ret; ret = sprintf(buffer, "DMA dump type: selected(*)\n"); ret += sprintf(buffer + ret, dbg_dma_dump_type == DDD_TYPE_PERIOD ? "\t- period(0)*\n" : "\t- period(0)\n"); ret += sprintf(buffer + ret, dbg_dma_dump_type == DDD_TYPE_INTERVAL ? "\t- interval(1)*\n" : "\t- interval(1)\n"); ret += sprintf(buffer + ret, dbg_dma_dump_type == DDD_TYPE_ONESHOT ? "\t- oneshot(2)*\n" : "\t- oneshot(2)\n"); return ret; } static const struct kernel_param_ops param_ops_dbg_dma_dump_type = { .set = param_set_uint, .get = param_get_dbg_dma_dump_type, }; module_param_cb(dma_dump_ctrl, ¶m_ops_dbg_dma_dump_ctrl, &dbg_dma_dump_ctrl, S_IRUGO | S_IWUSR); module_param_cb(dma_dump_type, ¶m_ops_dbg_dma_dump_type, &dbg_dma_dump_type, S_IRUGO | S_IWUSR); module_param_named(dma_dump_arg1, dbg_dma_dump_arg1, uint, S_IRUGO | S_IWUSR); module_param_named(dma_dump_arg2, dbg_dma_dump_arg2, uint, S_IRUGO | S_IWUSR); static void setup_plane_widths(struct is_frame_cfg *fc, u32 widths[], int cnt) { int p; for (p = 0; p < cnt ; p++) widths[p] = max(fc->width * fc->format->bitsperpixel[p] / BITS_PER_BYTE, fc->bytesperline[p]); } /* * V4L2_PIX_FMT_YUV444, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, * V4L2_PIX_FMT_Y10, V4L2_PIX_FMT_Y12, * V4L2_PIX_FMT_JPEG, * V4L2_PIX_FMT_Z16, */ static void widths0_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 1); for (p = 0; p < num_i_planes; p++) sizes[p] = widths[0] * fc->height; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_Y10BPACK */ static void aligned_widths0_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 1); for (p = 0; p < num_i_planes; p++) sizes[p] = ALIGN(widths[0], 16) * fc->height; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV16, V4L2_PIX_FMT_NV61 */ static void widths01_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p++) sizes[p] = (widths[0] + widths[1]) * fc->height; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV21 */ static void widths01_420_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p++) sizes[p] = widths[0] * fc->height + widths[1] * fc->height / 2; sizes[p] = SIZE_OF_META_PLANE; } /* * V4L2_PIX_FMT_NV16M, V4L2_PIX_FMT_NV61M, * V4L2_PIX_FMT_Y8I */ static void widths01_2p_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = widths[0] * fc->height; sizes[p + 1] = widths[1] * fc->height; } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV21M */ static void widths01_2p_420_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = widths[0] * fc->height; sizes[p + 1] = widths[1] * fc->height / 2; } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV16M_P210 */ static void aligned_widths01_2p_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = ALIGN(widths[0], 16) * fc->height; sizes[p + 1] = ALIGN(widths[1], 16) * fc->height; } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV12M_P010 */ static void aligned_widths01_2p_420_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 2); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = ALIGN(widths[0], 16) * fc->height; sizes[p + 1] = ALIGN(widths[1], 16) * fc->height / 2; } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV16M_S10B */ static void aligned_2p_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = NV16M_Y_SIZE(fc->width, fc->height) + NV16M_Y_2B_SIZE(fc->width, fc->height); sizes[p + 1] = NV16M_CBCR_SIZE(fc->width, fc->height) + NV16M_CBCR_2B_SIZE(fc->width, fc->height); } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_NV12M_S10B */ static void aligned_2p_420_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p+=2) { sizes[p] = NV12M_Y_SIZE(fc->width, fc->height) + NV12M_Y_2B_SIZE(fc->width, fc->height); sizes[p + 1] = NV12M_CBCR_SIZE(fc->width, fc->height) + NV12M_CBCR_2B_SIZE(fc->width, fc->height); } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420 */ static void widths012_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 3); for (p = 0; p < num_i_planes; p++) sizes[p] = widths[0] * fc->height + widths[1] * fc->height / 2 + widths[2] * fc->height / 2; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_YUV420M, V4L2_PIX_FMT_YVU420M */ static void widths012_3p_420_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, 3); for (p = 0; p < num_i_planes; p+=3) { sizes[p] = widths[0] * fc->height; sizes[p + 1] = widths[1] * fc->height / 2; sizes[p + 2] = widths[2] * fc->height / 2; } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_YUV32 */ static void widthsp_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; u32 widths[IS_MAX_PLANES]; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); setup_plane_widths(fc, widths, num_i_planes); for (p = 0; p < num_i_planes; p++) sizes[p] = widths[p] * fc->height; sizes[p] = SIZE_OF_META_PLANE; } /* * V4L2_PIX_FMT_SGRBG8, V4L2_PIX_FMT_SBGGR8, * V4L2_PIX_FMT_GREY */ static void default_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = fc->width * fc->height; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_BGR24 */ static void default_x3_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = fc->width * fc->height * 3; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_RGB32 */ static void default_x4_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = fc->width * fc->height * 4; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SBGGR10, V4L2_PIX_FMT_SBGGR10P */ #define ALIGN_SBGGR10(w) ALIGN(((w) * 5) >> 2, 16) static void sbggr10x_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) { sizes[p] = ALIGN_SBGGR10(fc->width) * fc->height; if (fc->bytesperline[0]) { if (fc->bytesperline[0] >= ALIGN_SBGGR10(fc->width)) sizes[p] = fc->bytesperline[0] * fc->height; else err("bytesperline is too small" " (%s, width: %d, bytesperline: %d)", fmt->name, fc->width, fc->bytesperline[0]); } } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SBGGR12, V4L2_PIX_FMT_SBGGR12P */ #define ALIGN_SBGGR12(w) ALIGN(((w) * 3) >> 1, 16) static void sbggr12x_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) { sizes[p] = ALIGN_SBGGR12(fc->width) * fc->height; if (fc->bytesperline[0]) { if (fc->bytesperline[0] >= ALIGN_SBGGR12(fc->width)) sizes[p] = fc->bytesperline[0] * fc->height; else err("bytesperline is too small" " (%s, width: %d, bytesperline: %d)", fmt->name, fc->width, fc->bytesperline[0]); } } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SBGGR16 */ static void sbggr16_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) { sizes[p] = fc->width * fc->height * 2; if (fc->bytesperline[0]) { if (fc->bytesperline[0] >= fc->width * 2) sizes[p] = fc->bytesperline[0] * fc->height; else err("bytesperline is too small" " (%s, width: %d, bytesperline: %d)", fmt->name, fc->width, fc->bytesperline[0]); } } sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SRGB24_SP */ static void srgb24_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = ALIGN(fc->width * fc->height, 16) * 3; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SRGB36P_SP */ static void srgb36p_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = ALIGN(fc->width * fc->height * 12 / 8, 16) * 3; sizes[p] = SIZE_OF_META_PLANE; } /* V4L2_PIX_FMT_SRGB36_SP */ static void srgb36_sps(struct is_frame_cfg *fc, unsigned int sizes[]) { struct is_fmt *fmt = fc->format; int num_i_planes = fmt->num_planes - NUM_OF_META_PLANE; int p; dbg("%s, w:%d x h:%d\n", fmt->name, fc->width, fc->height); for (p = 0; p < num_i_planes; p++) sizes[p] = ALIGN(fc->width * fc->height * 2, 16) * 3; sizes[p] = SIZE_OF_META_PLANE; } static struct is_fmt is_formats[] = { { .name = "YUV 4:4:4 packed, YCbCr", .pixelformat = V4L2_PIX_FMT_YUV444, .num_planes = 1 + NUM_OF_META_PLANE, .mbus_code = 0, /* Not Defined */ .bitsperpixel = { 24 }, .hw_format = DMA_INOUT_FORMAT_YUV444, .hw_order = DMA_INOUT_ORDER_YCbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "YUV 4:2:2 packed, YCbYCr", .pixelformat = V4L2_PIX_FMT_YUYV, .num_planes = 1 + NUM_OF_META_PLANE, .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_YCbYCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "YUV 4:2:2 packed, YCbYCr", .pixelformat = V4L2_PIX_FMT_YUYV, .num_planes = 1 + NUM_OF_META_PLANE, .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_YCbYCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "YUV 4:2:2 packed, CbYCrY", .pixelformat = V4L2_PIX_FMT_UYVY, .num_planes = 1 + NUM_OF_META_PLANE, .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbYCrY, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "YUV 4:2:2 planar, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV16, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_sps, }, { .name = "YUV 4:2:2 planar, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV61, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CrCb, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_sps, }, { .name = "YUV 4:2:2 non-contiguous 2-planar,, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV16M, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_2p_sps, }, { .name = "YUV 4:2:2 non-contiguous 2-planar, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV61M, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CrCb, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_2p_sps, }, { .name = "YUV 4:2:2 planar, Y/Cb/Cr", .pixelformat = V4L2_PIX_FMT_YUV422P, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = widths012_sps, }, { .name = "YUV 4:2:0 planar, YCbCr", .pixelformat = V4L2_PIX_FMT_YUV420, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 4, 4 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = widths012_sps, }, { .name = "YUV 4:2:0 planar, YCbCr", .pixelformat = V4L2_PIX_FMT_YVU420, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 4, 4 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = widths012_sps, }, { .name = "YUV 4:2:0 planar, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV12, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_420_sps, }, { .name = "YUV 4:2:0 planar, Y/CrCb", .pixelformat = V4L2_PIX_FMT_NV21, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CrCb, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_420_sps, }, { .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", .pixelformat = V4L2_PIX_FMT_NV12M, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_2p_420_sps, }, { .name = "YVU 4:2:0 non-contiguous 2-planar, Y/CrCb", .pixelformat = V4L2_PIX_FMT_NV21M, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CrCb, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_2p_420_sps, }, { .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", .pixelformat = V4L2_PIX_FMT_YUV420M, .num_planes = 3 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 4, 4 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = widths012_3p_420_sps, }, { .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb", .pixelformat = V4L2_PIX_FMT_YVU420M, .num_planes = 3 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 4, 4 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = widths012_3p_420_sps, }, { .name = "BAYER 8 bit(GRBG)", .pixelformat = V4L2_PIX_FMT_SGRBG8, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8 }, .hw_format = DMA_INOUT_FORMAT_BAYER, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, /* memory bitwidth */ .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_sps, }, { .name = "BAYER 8 bit(BA81)", .pixelformat = V4L2_PIX_FMT_SBGGR8, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8 }, .hw_format = DMA_INOUT_FORMAT_BAYER, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, /* memory bitwidth */ .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_sps, }, { .name = "BAYER 10 bit", .pixelformat = V4L2_PIX_FMT_SBGGR10, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 10 }, .hw_format = DMA_INOUT_FORMAT_BAYER, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, /* memory bitwidth */ .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = sbggr10x_sps, }, { .name = "BAYER 12 bit", .pixelformat = V4L2_PIX_FMT_SBGGR12, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 12 }, .hw_format = DMA_INOUT_FORMAT_BAYER, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, /* memory bitwidth */ .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = sbggr12x_sps, }, { .name = "BAYER 16 bit", .pixelformat = V4L2_PIX_FMT_SBGGR16, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_BAYER, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, /* memory bitwidth */ .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = sbggr16_sps, }, { .name = "BAYER 10 bit packed", .pixelformat = V4L2_PIX_FMT_SBGGR10P, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 10 }, .hw_format = DMA_INOUT_FORMAT_BAYER_PACKED, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = sbggr10x_sps, }, { .name = "BAYER 12 bit packed", .pixelformat = V4L2_PIX_FMT_SBGGR12P, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 12 }, .hw_format = DMA_INOUT_FORMAT_BAYER_PACKED, .hw_order = DMA_INOUT_ORDER_GB_BG, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_12BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = sbggr12x_sps, }, { .name = "ARGB32", .pixelformat = V4L2_PIX_FMT_ARGB32, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 32 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_ARGB, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_32BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_x4_sps, }, { .name = "BGRA32", .pixelformat = V4L2_PIX_FMT_BGRA32, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 32 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_BGRA, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_32BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_x4_sps, }, { .name = "RGBA32", .pixelformat = V4L2_PIX_FMT_RGBA32, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 32 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_RGBA, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_32BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_x4_sps, }, { .name = "ABGR32", .pixelformat = V4L2_PIX_FMT_ABGR32, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 32 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_BGRA, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_32BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_x4_sps, }, { .name = "RGB24 planar", .pixelformat = V4L2_PIX_FMT_RGB24, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 24 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_BGR, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = default_x3_sps, }, { .name = "BGR24 planar", .pixelformat = V4L2_PIX_FMT_BGR24, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 24 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_BGR, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = default_x3_sps, }, { .name = "BAYER 12bit packed single plane", .pixelformat = V4L2_PIX_FMT_SRGB36P_SP, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 12 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_12BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = srgb36p_sps, }, { .name = "BAYER 12bit unpacked single plane", .pixelformat = V4L2_PIX_FMT_SRGB36_SP, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = srgb36_sps, }, { .name = "BAYER 8bit single plane", .pixelformat = V4L2_PIX_FMT_SRGB24_SP, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8 }, .hw_format = DMA_INOUT_FORMAT_RGB, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_3, .setup_plane_sz = srgb24_sps, }, { .name = "Y 8bit", .pixelformat = V4L2_PIX_FMT_GREY, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 8 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = default_sps, }, { .name = "Y 10bit", .pixelformat = V4L2_PIX_FMT_Y10, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "Y 12bit", .pixelformat = V4L2_PIX_FMT_Y12, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 16 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = widths0_sps, }, { .name = "Y Packed 10bit", .pixelformat = V4L2_PIX_FMT_Y10BPACK, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 10 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_1, .setup_plane_sz = aligned_widths0_sps, }, { .name = "Y L/R interleaved 8bit", .pixelformat = V4L2_PIX_FMT_Y8I, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widths01_2p_sps, }, { .name = "YUV32", .pixelformat = V4L2_PIX_FMT_YUV32, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_Y, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_32BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = widthsp_sps, }, { .name = "P210_16B", .pixelformat = V4L2_PIX_FMT_NV16M_P210, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 16, 16 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = aligned_widths01_2p_sps, }, { .name = "P210_12B", .pixelformat = V4L2_PIX_FMT_NV16M_P210, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 12, 12 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_12BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = aligned_widths01_2p_sps, }, { .name = "P210_10B", .pixelformat = V4L2_PIX_FMT_NV16M_P210, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 10, 10 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = aligned_widths01_2p_sps, }, { .name = "P010_16B", .pixelformat = V4L2_PIX_FMT_NV12M_P010, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 16, 16 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = aligned_widths01_2p_420_sps, }, { .name = "P010_10B", .pixelformat = V4L2_PIX_FMT_NV12M_P010, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 10, 10 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_2, .setup_plane_sz = aligned_widths01_2p_420_sps, }, { .name = "YUV422 2P 10bit(8+2)", .pixelformat = V4L2_PIX_FMT_NV16M_S10B, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV422, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_4, .setup_plane_sz = aligned_2p_sps, }, { .name = "YUV420 2P 10bit(8+2)", .pixelformat = V4L2_PIX_FMT_NV12M_S10B, .num_planes = 2 + NUM_OF_META_PLANE, .bitsperpixel = { 8, 8 }, .hw_format = DMA_INOUT_FORMAT_YUV420, .hw_order = DMA_INOUT_ORDER_CbCr, .hw_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT, .hw_plane = DMA_INOUT_PLANE_4, .setup_plane_sz = aligned_2p_420_sps, }, { .name = "JPEG", .pixelformat = V4L2_PIX_FMT_JPEG, .num_planes = 1 + NUM_OF_META_PLANE, .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, .bitsperpixel = { 8 }, .hw_format = 0, .hw_order = 0, .hw_bitwidth = 0, .hw_plane = 0, .setup_plane_sz = widths0_sps, }, { .name = "DEPTH", .pixelformat = V4L2_PIX_FMT_Z16, .num_planes = 1 + NUM_OF_META_PLANE, .bitsperpixel = { 32 }, .hw_format = 0, .hw_order = DMA_INOUT_ORDER_NO, .hw_bitwidth = 0, .hw_plane = 0, .setup_plane_sz = widths0_sps, } }; #if IS_ENABLED(CONFIG_PABLO_KUNIT_TEST) struct is_fmt *pablo_kunit_get_is_formats_struct(ulong index) { if (ARRAY_SIZE(is_formats) <= index) return NULL; return &is_formats[index]; } KUNIT_EXPORT_SYMBOL(pablo_kunit_get_is_formats_struct); ulong pablo_kunit_get_array_size_is_formats(void) { return ARRAY_SIZE(is_formats); } KUNIT_EXPORT_SYMBOL(pablo_kunit_get_array_size_is_formats); #endif struct is_fmt *is_find_format(u32 pixelformat, u32 flags) { ulong i; struct is_fmt *result, *fmt; u8 pixel_size; u32 memory_bitwidth; if (!pixelformat) { err("pixelformat is null"); return NULL; } pixel_size = flags & PIXEL_TYPE_SIZE_MASK; if (pixel_size == CAMERA_PIXEL_SIZE_10BIT || pixel_size == CAMERA_PIXEL_SIZE_12BIT) memory_bitwidth = DMA_INOUT_BIT_WIDTH_16BIT; else if (pixel_size == CAMERA_PIXEL_SIZE_PACKED_12BIT) memory_bitwidth = DMA_INOUT_BIT_WIDTH_12BIT; else if (pixel_size == CAMERA_PIXEL_SIZE_PACKED_10BIT) memory_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT; else if (pixel_size == CAMERA_PIXEL_SIZE_8_2BIT) memory_bitwidth = DMA_INOUT_BIT_WIDTH_10BIT; else memory_bitwidth = DMA_INOUT_BIT_WIDTH_8BIT; result = NULL; for (i = 0; i < ARRAY_SIZE(is_formats); ++i) { fmt = &is_formats[i]; if (fmt->pixelformat == pixelformat) { if (pixelformat == V4L2_PIX_FMT_NV16M_P210 || pixelformat == V4L2_PIX_FMT_NV12M_P010 || pixelformat == V4L2_PIX_FMT_YUYV) { if (fmt->hw_bitwidth != memory_bitwidth) continue; } result = fmt; break; } } return result; } static inline void vref_init(struct is_video *video) { atomic_set(&video->refcount, 0); } static inline int vref_get(struct is_video *video) { return atomic_inc_return(&video->refcount) - 1; } static inline int vref_put(struct is_video *video, void (*release)(struct is_video *video)) { int ret = 0; ret = atomic_sub_and_test(1, &video->refcount); if (ret) pr_debug("closed all instacne"); return atomic_read(&video->refcount); } static int queue_init(void *priv, struct vb2_queue *vbq, struct vb2_queue *vbq_dst) { int ret = 0; struct is_video_ctx *vctx = priv; struct is_video *video; u32 type; FIMC_BUG(!vctx); FIMC_BUG(!GET_VIDEO(vctx)); FIMC_BUG(!vbq); video = GET_VIDEO(vctx); if (video->video_type == IS_VIDEO_TYPE_CAPTURE) type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; else type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; vbq->type = type; vbq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; vbq->drv_priv = vctx; vbq->buf_struct_size = sizeof(struct is_vb2_buf); vbq->ops = video->vb2_ops; vbq->mem_ops = video->vb2_mem_ops; vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(vbq); if (ret) { mverr("vb2_queue_init fail(%d)", vctx, video, ret); goto p_err; } vctx->queue.vbq = vbq; p_err: return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Pablo queue operaions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int is_queue_open(struct is_queue *queue, u32 rdycount) { int ret = 0; queue->buf_maxcount = 0; queue->buf_refcount = 0; queue->buf_rdycount = rdycount; queue->buf_req = 0; queue->buf_pre = 0; queue->buf_que = 0; queue->buf_com = 0; queue->buf_dqe = 0; clear_bit(IS_QUEUE_BUFFER_PREPARED, &queue->state); clear_bit(IS_QUEUE_BUFFER_READY, &queue->state); clear_bit(IS_QUEUE_STREAM_ON, &queue->state); clear_bit(IS_QUEUE_NEED_TO_REMAP, &queue->state); clear_bit(IS_QUEUE_NEED_TO_KMAP, &queue->state); clear_bit(IS_QUEUE_NEED_TO_EXTMAP, &queue->state); memset(&queue->framecfg, 0, sizeof(struct is_frame_cfg)); frame_manager_probe(&queue->framemgr, queue->id, queue->name); return ret; } static int is_queue_close(struct is_queue *queue) { int ret = 0; queue->buf_maxcount = 0; queue->buf_refcount = 0; clear_bit(IS_QUEUE_BUFFER_PREPARED, &queue->state); clear_bit(IS_QUEUE_BUFFER_READY, &queue->state); clear_bit(IS_QUEUE_STREAM_ON, &queue->state); clear_bit(IS_QUEUE_NEED_TO_REMAP, &queue->state); clear_bit(IS_QUEUE_NEED_TO_KMAP, &queue->state); clear_bit(IS_QUEUE_NEED_TO_EXTMAP, &queue->state); frame_manager_close(&queue->framemgr); return ret; } static int is_queue_set_format_mplane(struct is_video_ctx *vctx, struct is_queue *queue, void *device, struct v4l2_format *format) { int ret = 0; u32 plane; struct v4l2_pix_format_mplane *pix; struct is_fmt *fmt; struct is_video *video; FIMC_BUG(!queue); video = GET_VIDEO(vctx); pix = &format->fmt.pix_mp; fmt = is_find_format(pix->pixelformat, pix->flags); if (!fmt) { mverr("[%s] pixel format is not found", vctx, video, queue->name); ret = -EINVAL; goto p_err; } queue->framecfg.format = fmt; queue->framecfg.colorspace = pix->colorspace; queue->framecfg.quantization = pix->quantization; queue->framecfg.width = pix->width; queue->framecfg.height = pix->height; queue->framecfg.hw_pixeltype = pix->flags; for (plane = 0; plane < fmt->hw_plane; ++plane) { if (pix->plane_fmt[plane].bytesperline) { queue->framecfg.bytesperline[plane] = pix->plane_fmt[plane].bytesperline; } else { queue->framecfg.bytesperline[plane] = 0; } } ret = CALL_QOPS(queue, s_fmt, device, queue); if (ret) { mverr("[%s] s_fmt is fail(%d)", vctx, video, queue->name, ret); goto p_err; } mvinfo("[%s]pixelformat(%c%c%c%c) bit(%d) size(%dx%d) flag(0x%x)\n", vctx, video, queue->name, (char)((fmt->pixelformat >> 0) & 0xFF), (char)((fmt->pixelformat >> 8) & 0xFF), (char)((fmt->pixelformat >> 16) & 0xFF), (char)((fmt->pixelformat >> 24) & 0xFF), queue->framecfg.format->hw_bitwidth, queue->framecfg.width, queue->framecfg.height, queue->framecfg.hw_pixeltype); p_err: return ret; } static void is_queue_subbuf_draw_digit(struct is_queue *queue, struct is_frame *frame) { struct is_debug_dma_info dinfo; struct is_sub_node *snode = &frame->cap_node; struct is_sub_dma_buf *sdbuf; struct camera2_node *node; struct is_fmt *fmt; u32 n, b; /* Only handle 1st plane */ for (n = 0; n < CAPTURE_NODE_MAX; n++) { node = &frame->shot_ext->node_group.capture[n]; if (!node->request) continue; sdbuf = &queue->out_buf[frame->index].sub[n]; fmt = is_find_format(node->pixelformat, node->flags); if (!fmt) { warn("[%s][I%d][F%d] pixelformat(%c%c%c%c) is not found", queue->name, frame->index, frame->fcount, (char)((node->pixelformat >> 0) & 0xFF), (char)((node->pixelformat >> 8) & 0xFF), (char)((node->pixelformat >> 16) & 0xFF), (char)((node->pixelformat >> 24) & 0xFF)); continue; } dinfo.width = node->output.cropRegion[2]; dinfo.height = node->output.cropRegion[3]; dinfo.pixeltype = node->flags; dinfo.bpp = fmt->bitsperpixel[0]; dinfo.pixelformat = fmt->pixelformat; for (b = 0; b < sdbuf->num_buffer; b++) { dinfo.addr = snode->sframe[n].kva[b * sdbuf->num_plane]; if (dinfo.addr) is_dbg_draw_digit(&dinfo, frame->fcount); } } } static void is_queue_draw_digit(struct is_queue *queue, struct is_vb2_buf *vbuf) { struct is_debug_dma_info dinfo; struct is_video_ctx *vctx = container_of(queue, struct is_video_ctx, queue); struct is_video *video = GET_VIDEO(vctx); struct is_frame_cfg *framecfg = &queue->framecfg; struct is_frame *frame; u32 index = vbuf->vb.vb2_buf.index; u32 num_buffer = vbuf->num_merged_dbufs ? vbuf->num_merged_dbufs : 1; u32 num_i_planes = vbuf->vb.vb2_buf.num_planes - NUM_OF_META_PLANE; u32 num_ext_planes = 0; u32 b; if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &vctx->queue.state)) num_ext_planes = num_i_planes - vctx->queue.framecfg.format->hw_plane; num_i_planes -= num_ext_planes; frame = &queue->framemgr.frames[index]; /* Draw digit on capture node buffer */ if (video->video_type == IS_VIDEO_TYPE_CAPTURE) { dinfo.width = frame->width ? frame->width : framecfg->width; dinfo.height = frame->height ? frame->height : framecfg->height; dinfo.pixeltype = framecfg->hw_pixeltype; dinfo.bpp = framecfg->format->bitsperpixel[0]; dinfo.pixelformat = framecfg->format->pixelformat; for (b = 0; b < num_buffer; b++) { dinfo.addr = queue->buf_kva[index][b * num_i_planes]; if (dinfo.addr) is_dbg_draw_digit(&dinfo, frame->fcount); } } /* Draw digit on LVN sub node buffers */ if (IS_ENABLED(LOGICAL_VIDEO_NODE) && video->video_type == IS_VIDEO_TYPE_LEADER && queue->mode == CAMERA_NODE_LOGICAL) is_queue_subbuf_draw_digit(queue, frame); } static int _is_queue_subbuf_prepare(struct device *dev, struct is_vb2_buf *vbuf, struct camera2_node_group *node_group, bool need_vmap) { int ret; struct vb2_buffer *vb = &vbuf->vb.vb2_buf; struct is_video_ctx *vctx = vb->vb2_queue->drv_priv; struct is_video *video = GET_VIDEO(vctx); struct is_queue *queue = GET_QUEUE(vctx); struct is_device_ischain *device = GET_DEVICE_ISCHAIN(vctx); struct camera2_node *node; struct is_sub_buf *sbuf; struct v4l2_plane planes[IS_MAX_PLANES]; struct is_sub_dma_buf *sdbuf; u32 index = vb->index; u32 num_i_planes, n; unsigned int dbg_draw_digit_ctrl; struct is_mem *mem; mdbgv_lvn(3, "[%s][I%d]subbuf_prepare: queue 0x%lx\n", vctx, video, queue->name, index, queue); /* Extract input subbuf */ node = &node_group->leader; if (!node->request) return 0; if (node->vid >= IS_VIDEO_MAX_NUM) { mverr("[%s][I%d]subbuf_prepare: invalid input (req:%d, vid:%d, length:%d)\n", vctx, video, queue->name, index, node->request, node->vid, node->buf.length); } node->result = 1; dbg_draw_digit_ctrl = is_get_digit_ctrl(); /* Disable SBWC for drawing digit on it */ if (dbg_draw_digit_ctrl) node->flags &= ~PIXEL_TYPE_EXTRA_MASK; #ifdef ENABLE_LVN_DUMMYOUTPUT num_i_planes = node->buf.length - NUM_OF_META_PLANE; sbuf = &queue->in_buf[index]; sbuf->ldr_vid = video->id; sdbuf = &sbuf->sub[0]; sdbuf->vid = node->vid; sdbuf->num_plane = num_i_planes; if (copy_from_user(planes, node->buf.m.planes, sizeof(struct v4l2_plane) * num_i_planes) != 0) { mverr("[%s][%s][I%d] Failed copy_from_user", vctx, video, queue->name, vn_name[node->vid], index); return -EFAULT; } mem = is_hw_get_iommu_mem(node->vid); vbuf->ops->subbuf_prepare(sdbuf, planes, mem->dev); ret = vbuf->ops->subbuf_dvmap(sdbuf); if (ret) { mverr("[%s][%s][I%d]Failed to get dva", vctx, video, queue->name, vn_name[node->vid], index); return ret; } #endif /* Extract output subbuf */ for (n = 0; n < CAPTURE_NODE_MAX; n++) { node = &node_group->capture[n]; if (!node->request) continue; if (node->buf.length > IS_MAX_PLANES || node->vid >= IS_VIDEO_MAX_NUM) { mverr("[%s][I%d]subbuf_prepare: invalid output[%d] (req:%d, vid:%d, length:%d) input (req:%d, vid:%d)\n", vctx, video, queue->name, index, n, node->request, node->vid, node->buf.length, node_group->leader.request, node_group->leader.vid); /* * FIXME : * For normal error handling, it would be nice to return error to user * but condition of system need to be preserved because of some issues * So, temporarily BUG() is used here. */ BUG(); } else if (!node->buf.length) { mdbgv_lvn(2, "[%s][%s][I%d]subbuf_prepare: Invalid buf.length %d", vctx, video, queue->name, vn_name[node->vid], index, node->buf.length); continue; } node->result = 1; num_i_planes = node->buf.length - NUM_OF_META_PLANE; sbuf = &queue->out_buf[index]; sbuf->ldr_vid = video->id; sdbuf = &sbuf->sub[n]; sdbuf->vid = node->vid; sdbuf->num_plane = num_i_planes; if (copy_from_user(planes, node->buf.m.planes, sizeof(struct v4l2_plane) * num_i_planes) != 0) { mverr("[%s][%s][I%d] Failed copy_from_user", vctx, video, queue->name, vn_name[node->vid], index); continue; } mem = is_hw_get_iommu_mem(node->vid); vbuf->ops->subbuf_prepare(sdbuf, planes, mem->dev); ret = vbuf->ops->subbuf_dvmap(sdbuf); if (ret) { mverr("[%s][%s][I%d]Failed to get dva", vctx, video, queue->name, vn_name[node->vid], index); return ret; } if (need_vmap || CHECK_NEED_KVADDR_ID(sdbuf->vid)) { ret = vbuf->ops->subbuf_kvmap(sdbuf); if (ret) { mverr("[%s][%s][I%d]Failed to get dva", vctx, video, queue->name, vn_name[node->vid], index); return ret; } /* need cache maintenance */ is_q_dbuf_q(device->dbuf_q, sdbuf, vb->vb2_queue->queued_count); /* Disable SBWC for drawing digit on it */ if (dbg_draw_digit_ctrl) node->flags &= ~PIXEL_TYPE_EXTRA_MASK; } } return 0; } int _is_queue_buffer_tag(dma_addr_t saddr[], dma_addr_t taddr[], u32 pixelformat, u32 width, u32 height, u32 planes, u32 *hw_planes) { int i, j; int ret = 0; *hw_planes = planes; switch (pixelformat) { case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: for (i = 0; i < planes; i++) { j = i * 2; taddr[j] = saddr[i]; taddr[j + 1] = taddr[j] + (width * height); } *hw_planes = planes * 2; break; case V4L2_PIX_FMT_YVU420M: for (i = 0; i < planes; i += 3) { taddr[i] = saddr[i]; taddr[i + 1] = saddr[i + 2]; taddr[i + 2] = saddr[i + 1]; } break; case V4L2_PIX_FMT_YUV420: for (i = 0; i < planes; i++) { j = i * 3; taddr[j] = saddr[i]; taddr[j + 1] = taddr[j] + (width * height); taddr[j + 2] = taddr[j + 1] + (width * height / 4); } *hw_planes = planes * 3; break; case V4L2_PIX_FMT_YVU420: /* AYV12 spec: The width should be aligned by 16 pixel. */ for (i = 0; i < planes; i++) { j = i * 3; taddr[j] = saddr[i]; taddr[j + 2] = taddr[j] + (ALIGN(width, 16) * height); taddr[j + 1] = taddr[j + 2] + (ALIGN(width / 2, 16) * height / 2); } *hw_planes = planes * 3; break; case V4L2_PIX_FMT_YUV422P: for (i = 0; i < planes; i++) { j = i * 3; taddr[j] = saddr[i]; taddr[j + 1] = taddr[j] + (width * height); taddr[j + 2] = taddr[j + 1] + (width * height / 2); } *hw_planes = planes * 3; break; case V4L2_PIX_FMT_NV12M_S10B: case V4L2_PIX_FMT_NV21M_S10B: for (i = 0; i < planes; i += 2) { j = i * 2; /* Y_ADDR, UV_ADDR, Y_2BIT_ADDR, UV_2BIT_ADDR */ taddr[j] = saddr[i]; taddr[j + 1] = saddr[i + 1]; taddr[j + 2] = taddr[j] + NV12M_Y_SIZE(width, height); taddr[j + 3] = taddr[j + 1] + NV12M_CBCR_SIZE(width, height); } break; case V4L2_PIX_FMT_NV16M_S10B: case V4L2_PIX_FMT_NV61M_S10B: for (i = 0; i < planes; i += 2) { j = i * 2; /* Y_ADDR, UV_ADDR, Y_2BIT_ADDR, UV_2BIT_ADDR */ taddr[j] = saddr[i]; taddr[j + 1] = saddr[i + 1]; taddr[j + 2] = taddr[j] + NV16M_Y_SIZE(width, height); taddr[j + 3] = taddr[j + 1] + NV16M_CBCR_SIZE(width, height); } break; case V4L2_PIX_FMT_RGB24: for (i = 0; i < planes; i++) { j = i * 3; taddr[j + 2] = saddr[i]; taddr[j + 1] = taddr[j + 2] + (width * height); taddr[j] = taddr[j + 1] + (width * height); } *hw_planes = planes * 3; break; case V4L2_PIX_FMT_BGR24: for (i = 0; i < planes; i++) { j = i * 3; taddr[j] = saddr[i]; taddr[j + 1] = taddr[j] + (width * height); taddr[j + 2] = taddr[j + 1] + (width * height); } *hw_planes = planes * 3; break; default: for (i = 0; i < planes; i++) taddr[i] = saddr[i]; break; } return ret; } static int _is_queue_subbuf_queue(struct is_video_ctx *vctx, struct is_queue *queue, struct is_frame *frame) { struct is_video *video = GET_VIDEO(vctx); struct camera2_node *node = NULL; struct is_sub_node * snode = NULL; struct is_sub_dma_buf *sdbuf; struct is_crop *crop; u32 index = frame->index; u32 n, stride_w, stride_h, hw_planes = 0; #ifdef ENABLE_LVN_DUMMYOUTPUT /* Extract input subbuf information */ node = &frame->shot_ext->node_group.leader; if (!node->request) return 0; snode = &frame->out_node; if (is_hw_get_output_slot(node->vid) < 0) { mverr("[%s][I%d]Invalid vid %d", vctx, video, queue->name, index, node->vid); return -EINVAL; } sdbuf = &queue->in_buf[index].sub[0]; /* Setup input subframe */ snode->sframe[0].id = sdbuf->vid; snode->sframe[0].num_planes = sdbuf->num_plane * sdbuf->num_buffer; crop = (struct is_crop *)node->input.cropRegion; stride_w = max(node->width, crop->w); stride_h = max(node->height, crop->h); _is_queue_buffer_tag(sdbuf->dva, snode->sframe[0].dva, node->pixelformat, stride_w, stride_h snode->sframe[0].num_planes, &hw_planes); snode->sframe[0].num_planes = hw_planes; #endif /* Extract output subbuf information */ snode = &frame->cap_node; for (n = 0; n < CAPTURE_NODE_MAX; n++) { /* clear first */ snode->sframe[n].id = 0; if (snode->sframe[n].kva[0]) memset(snode->sframe[n].kva, 0x0, sizeof(ulong) * snode->sframe[n].num_planes); node = &frame->shot_ext->node_group.capture[n]; if (!node->request) continue; sdbuf = &queue->out_buf[index].sub[n]; /* Setup output subframe */ snode->sframe[n].id = sdbuf->vid; snode->sframe[n].num_planes = sdbuf->num_plane * sdbuf->num_buffer; crop = (struct is_crop *)node->output.cropRegion; stride_w = max(node->width, crop->w); stride_h = max(node->height, crop->h); _is_queue_buffer_tag(sdbuf->dva, snode->sframe[n].dva, node->pixelformat, stride_w, stride_h, snode->sframe[n].num_planes, &hw_planes); snode->sframe[n].num_planes = hw_planes; if (sdbuf->kva[0]) { mdbgv_lvn(2, "[%s][%s] 0x%lx\n", vctx, video, queue->name, vn_name[node->vid], sdbuf->kva[0]); memcpy(snode->sframe[n].kva, sdbuf->kva, sizeof(ulong) * snode->sframe[n].num_planes); } mdbgv_lvn(4, "[%s][%s][N%d][I%d] pixelformat %c%c%c%c size %dx%d(%dx%d) length %d\n", vctx, video, queue->name, vn_name[node->vid], n, index, (char)((node->pixelformat >> 0) & 0xFF), (char)((node->pixelformat >> 8) & 0xFF), (char)((node->pixelformat >> 16) & 0xFF), (char)((node->pixelformat >> 24) & 0xFF), crop->w, crop->h, stride_w, stride_h, node->buf.length); } return 0; } int __find_mapped_index(__s32 target_fd, struct is_sub_buf buf[], int nidx, int pidx) { int ret = -1; #ifndef ENABLE_SKIP_PER_FRAME_MAP int j; for (j = 0; j < IS_MAX_BUFS; j++) { if (target_fd == buf[j].sub[nidx].buf_fd[pidx]) { ret = j; break; } } #endif return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Pablo vb2 operaions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static u32 get_instance_video_ctx(struct is_video_ctx *ivc) { struct is_video *iv = ivc->video; struct is_device_ischain *idi; struct is_device_sensor *ids; if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; return idi->instance; } else { ids = (struct is_device_sensor *)ivc->device; return ids->instance; } } static void mdbgv_video(struct is_video_ctx *ivc, struct is_video *iv, const char *msg) { if (iv->device_type == IS_DEVICE_ISCHAIN) mdbgv_ischain("%s\n", ivc, msg); else mdbgv_sensor("%s\n", ivc, msg); } int is_queue_setup(struct vb2_queue *vq, unsigned *nbuffers, unsigned *nplanes, unsigned sizes[], struct device *alloc_devs[]) { struct is_video_ctx *ivc = (struct is_video_ctx *)vq->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; struct is_frame_cfg *ifc = &iq->framecfg; struct is_fmt *fmt = ifc->format; int p; struct is_mem *mem; *nplanes = (unsigned int)fmt->num_planes; if (fmt->setup_plane_sz) { fmt->setup_plane_sz(ifc, sizes); } else { err("failed to setup plane sizes for pixelformat(%c%c%c%c)", (char)((fmt->pixelformat >> 0) & 0xFF), (char)((fmt->pixelformat >> 8) & 0xFF), (char)((fmt->pixelformat >> 16) & 0xFF), (char)((fmt->pixelformat >> 24) & 0xFF)); return -EINVAL; } /* FIXME: 1. share meta plane, 2. configure the size */ if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state)) { sizes[*nplanes] = SZ_256K + SZ_256K; *nplanes += NUM_OF_EXT_PLANE; } mem = is_hw_get_iommu_mem(iv->id); for (p = 0; p < *nplanes; p++) { alloc_devs[p] = mem->dev; ifc->size[p] = sizes[p]; mdbgv_vid("queue[%d] size : %d\n", p, sizes[p]); } mdbgv_video(ivc, iv, __func__); return 0; } void is_wait_prepare(struct vb2_queue *vbq) { struct is_video_ctx *ivc = (struct is_video_ctx *)vbq->drv_priv; struct is_video *iv = ivc->video; mutex_unlock(&iv->lock); } void is_wait_finish(struct vb2_queue *vbq) { struct is_video_ctx *ivc = (struct is_video_ctx *)vbq->drv_priv; struct is_video *iv = ivc->video; mutex_lock(&iv->lock); } int is_buf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb); struct is_vb2_buf *vbuf = vb_to_is_vb2_buf(vb2_v4l2_buf); struct is_video_ctx *ivc = (struct is_video_ctx *)vb->vb2_queue->drv_priv; struct is_video *iv = ivc->video; struct is_group *ig = ivc->group; struct is_queue *iq = &ivc->queue; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_groupmgr *grpmgr; u32 instance = get_instance_video_ctx(ivc); int ret; mvdbgs(3, "%s(%d)\n", ivc, iq, __func__, vb->index); /* for each leader */ if (iv->video_type == IS_VIDEO_TYPE_LEADER) { if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; grpmgr = idi->groupmgr; mdbgs_ischain(4, "%s\n", idi, __func__); } else { ids = (struct is_device_sensor *)ivc->device; grpmgr = ids->groupmgr; mdbgs_sensor(4, "%s\n", ids, __func__); } ret = pablo_group_buffer_init(grpmgr, ig, vb->index); if (ret) mierr("failure in pablo_group_buffer_init(): %d", instance, ret); /* for each non-leader */ } else { ret = pablo_subdev_buffer_init(ivc->subdev, vb); if (ret) mierr("failure in pablo_subdev_buffer_init(): %d", instance, ret); } vbuf->ops = iv->is_vb2_buf_ops; return 0; } int is_buf_prepare(struct vb2_buffer *vb) { int ret; struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb); struct is_vb2_buf *vbuf = vb_to_is_vb2_buf(vb2_v4l2_buf); struct is_video_ctx *vctx = (struct is_video_ctx *)vb->vb2_queue->drv_priv; struct is_queue *queue = GET_QUEUE(vctx); struct is_video *video = GET_VIDEO(vctx); unsigned int index = vb->index; unsigned int dbg_draw_digit_ctrl; struct is_frame *frame = &queue->framemgr.frames[index]; u32 num_i_planes = vb->num_planes - NUM_OF_META_PLANE; u32 num_buffers = 1, num_shots = 1, pos_meta_p, i; ulong kva_meta; bool need_vmap; struct is_mem *mem; dbg_draw_digit_ctrl = is_get_digit_ctrl(); /* take a snapshot whether it is needed or not */ need_vmap = (dbg_draw_digit_ctrl || ((dbg_dma_dump_ctrl & ~DDD_CTRL_META) && (video->vd.dev_debug & V4L2_DEV_DEBUG_DMA)) ); if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &queue->state)) num_i_planes -= NUM_OF_EXT_PLANE; /* Unmerged dma_buf_container */ if (IS_ENABLED(DMABUF_CONTAINER)) { mem = is_hw_get_iommu_mem(video->id); ret = vbuf->ops->dbufcon_prepare(vbuf, mem->dev); if (ret) { mverr("failed to prepare dmabuf-container: %d", vctx, video, index); return ret; } if (vbuf->num_merged_dbufs) { ret = vbuf->ops->dbufcon_map(vbuf); if (ret) { mverr("failed to map dmabuf-container: %d", vctx, video, index); vbuf->ops->dbufcon_finish(vbuf); return ret; } num_buffers = vbuf->num_merged_dbufs; } /* Unmerged meta plane */ ret = vbuf->ops->dbufcon_kmap(vbuf, num_i_planes); if (ret) { mverr("failed to kmap dmabuf-container: %d", vctx, video, index); return ret; } if (vbuf->num_merged_sbufs) num_shots = vbuf->num_merged_sbufs; } /* Get kva of image planes */ if (test_bit(IS_QUEUE_NEED_TO_KMAP, &queue->state)) { for (i = 0; i < num_i_planes; i++) vbuf->ops->plane_kmap(vbuf, i, 0); } else if (need_vmap) { if (vbuf->num_merged_dbufs) { for (i = 0; i < num_i_planes; i++) vbuf->ops->dbufcon_kmap(vbuf, i); } else { for (i = 0; i < num_i_planes; i++) vbuf->kva[i] = vbuf->ops->plane_kvaddr(vbuf, i); } } /* Disable SBWC for drawing digit on it */ if (dbg_draw_digit_ctrl) queue->framecfg.hw_pixeltype &= ~PIXEL_TYPE_EXTRA_MASK; /* Get metadata planes */ pos_meta_p = num_i_planes * num_buffers; if (num_shots > 1) { for (i = 0; i < num_shots; i++) queue->buf_kva[index][pos_meta_p + i] = vbuf->kva[pos_meta_p + i]; } else { kva_meta = vbuf->ops->plane_kmap(vbuf, num_i_planes, 0); if (!kva_meta) { mverr("[%s][I%d][P%d]Failed to get kva", vctx, video, queue->name, index, num_i_planes); return -ENOMEM; } queue->buf_kva[index][pos_meta_p] = kva_meta; } /* Setup frame */ frame->num_buffers = num_buffers; frame->planes = num_i_planes * num_buffers; frame->num_shots = num_shots; kva_meta = queue->buf_kva[index][pos_meta_p]; if (video->video_type == IS_VIDEO_TYPE_LEADER) { /* Output node */ frame->shot_ext = (struct camera2_shot_ext *)kva_meta; frame->shot = &frame->shot_ext->shot; frame->shot_size = sizeof(frame->shot); if (sizeof(struct camera2_shot_ext) > SIZE_OF_META_PLANE) { mverr("Meta size overflow %d", vctx, video, sizeof(struct camera2_shot_ext)); FIMC_BUG(1); } if (frame->shot->magicNumber != SHOT_MAGIC_NUMBER) { mverr("[%s][I%d]Shot magic number error! 0x%08X size %zd", vctx, video, queue->name, index, frame->shot->magicNumber, sizeof(struct camera2_shot_ext)); return -EINVAL; } } else { /* Capture node */ frame->stream = (struct camera2_stream *)kva_meta; /* TODO : Change type of address into ulong */ frame->stream->address = (u32)kva_meta; } if (IS_ENABLED(LOGICAL_VIDEO_NODE)) { queue->mode = CAMERA_NODE_LOGICAL; if (video->video_type == IS_VIDEO_TYPE_LEADER && queue->mode == CAMERA_NODE_LOGICAL) { ret = _is_queue_subbuf_prepare(video->alloc_dev, vbuf, &frame->shot_ext->node_group, need_vmap); if (ret) { mverr("[%s][I%d]Failed to subbuf_prepare", vctx, video, queue->name, index); return ret; } } } if (test_bit(IS_QUEUE_NEED_TO_REMAP, &queue->state)) { ret = vbuf->ops->remap_attr(vbuf, 0); if (ret) { mverr("failed to remap dmabuf: %d", vctx, video, index); return ret; } } queue->buf_pre++; return 0; } #define V4L2_BUF_FLAG_DISPOSAL 0x10000000 static void __is_buf_finish(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb); struct is_vb2_buf *ivb = vb_to_is_vb2_buf(vb2_v4l2_buf); struct vb2_queue *vbq = vb->vb2_queue; struct is_video_ctx *ivc = (struct is_video_ctx *)vb->vb2_queue->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; u32 framecount = iq->framemgr.frames[vb->index].fcount; bool ddd_trigger = false; unsigned int num_i_planes = vb->num_planes - NUM_OF_META_PLANE; unsigned int num_ext_planes = 0; int p; struct vb2_plane *vbp; #if !defined(ENABLE_SKIP_PER_FRAME_MAP) unsigned int index = vb->index; struct is_sub_buf *sbuf; struct is_sub_dma_buf *sdbuf; u32 n; #endif if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state)) num_ext_planes = num_i_planes - iq->framecfg.format->hw_plane; num_i_planes -= num_ext_planes; if (is_get_digit_ctrl()) is_queue_draw_digit(iq, ivb); if (iv->vd.dev_debug & V4L2_DEV_DEBUG_DMA) { if (dbg_dma_dump_type == DDD_TYPE_PERIOD) ddd_trigger = !(framecount % dbg_dma_dump_arg1); else if (dbg_dma_dump_type == DDD_TYPE_INTERVAL) ddd_trigger = (framecount >= dbg_dma_dump_arg1) && (framecount <= dbg_dma_dump_arg2); else if (dbg_dma_dump_type == DDD_TYPE_ONESHOT) ddd_trigger = (framecount == dbg_dma_dump_arg1); if (ddd_trigger && (dbg_dma_dump_ctrl & DDD_CTRL_IMAGE)) is_dbg_dma_dump(iq, ivc->instance, vb->index, iv->id, DBG_DMA_DUMP_IMAGE); if (ddd_trigger && (dbg_dma_dump_ctrl & DDD_CTRL_META)) is_dbg_dma_dump(iq, ivc->instance, vb->index, iv->id, DBG_DMA_DUMP_META); } if (IS_ENABLED(LOGICAL_VIDEO_NODE) && iq->mode == CAMERA_NODE_LOGICAL) { mdbgv_lvn(3, "[%s][I%d]buf_finish: queue 0x%lx\n", ivc, iv, vn_name[iv->id], index, iq); #ifndef ENABLE_SKIP_PER_FRAME_MAP #ifdef ENABLE_LVN_DUMMYOUTPUT /* release input */ sbuf = &iq->in_buf[index]; sdbuf = &sbuf->sub[0]; ivb->ops->subbuf_finish(sdbuf); sbuf->ldr_vid = 0; #endif /* release output */ for (n = 0; n < CAPTURE_NODE_MAX; n++) { sbuf = &iq->out_buf[index]; sdbuf = &sbuf->sub[n]; if (!sdbuf->vid) continue; if (sdbuf->kva[0]) ivb->ops->subbuf_kunmap(sdbuf); ivb->ops->subbuf_finish(sdbuf); sbuf->ldr_vid = 0; } #endif } if (IS_ENABLED(DMABUF_CONTAINER) && (ivb->num_merged_dbufs)) { for (p = 0; p < num_i_planes && ivb->kva[p]; p++) ivb->ops->dbufcon_kunmap(ivb, p); ivb->ops->dbufcon_unmap(ivb); ivb->ops->dbufcon_finish(ivb); } if (test_bit(IS_QUEUE_NEED_TO_REMAP, &iq->state)) ivb->ops->unremap_attr(ivb, 0); if ((vb2_v4l2_buf->flags & V4L2_BUF_FLAG_DISPOSAL) && vbq->memory == VB2_MEMORY_DMABUF) { is_buf_cleanup(vb); for (p = 0; p < vb->num_planes; p++) { vbp = &vb->planes[p]; if (!vbp->mem_priv) continue; if (vbp->dbuf_mapped) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) vbq->mem_ops->unmap_dmabuf(vbp->mem_priv); #else vbq->mem_ops->unmap_dmabuf(vbp->mem_priv, 0); #endif vbq->mem_ops->detach_dmabuf(vbp->mem_priv); dma_buf_put(vbp->dbuf); vbp->mem_priv = NULL; vbp->dbuf = NULL; vbp->dbuf_mapped = 0; } } } void is_buf_finish(struct vb2_buffer *vb) { struct is_video_ctx *ivc = (struct is_video_ctx *)vb->vb2_queue->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; struct is_group *ig = ivc->group; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_groupmgr *grpmgr; u32 instance = get_instance_video_ctx(ivc); int ret; mvdbgs(3, "%s(%d)\n", ivc, iq, __func__, vb->index); /* for each leader */ if (iv->video_type == IS_VIDEO_TYPE_LEADER) { if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; grpmgr = idi->groupmgr; mdbgs_ischain(4, "%s\n", idi, __func__); } else { ids = (struct is_device_sensor *)ivc->device; grpmgr = ids->groupmgr; mdbgs_sensor(4, "%s\n", ids, __func__); } ret = is_group_buffer_finish(grpmgr, ig, vb->index); if (ret) mierr("failure in is_group_buffer_finish(): %d", instance, ret); /* for each non-leader */ } else { ret = is_subdev_buffer_finish(ivc->subdev, vb); if (ret) mierr("failure in is_subdev_buffer_finish(): %d", instance, ret); } __is_buf_finish(vb); } void is_buf_cleanup(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb); struct is_vb2_buf *vbuf = vb_to_is_vb2_buf(vb2_v4l2_buf); struct is_video_ctx *vctx = (struct is_video_ctx *)vb->vb2_queue->drv_priv; unsigned int num_i_planes = vb->num_planes - NUM_OF_META_PLANE; unsigned int num_m_planes; unsigned int pos_ext_p; int i; if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &vctx->queue.state)) num_i_planes -= NUM_OF_EXT_PLANE; num_m_planes = vbuf->num_merged_sbufs ? vbuf->num_merged_sbufs : 1; /* FIXME: doesn't support dmabuf container yet */ if (test_bit(IS_QUEUE_NEED_TO_KMAP, &vctx->queue.state)) { for (i = 0; i < vb->num_planes; i++) vbuf->ops->plane_kunmap(vbuf, i, 0); } else { if (vbuf->num_merged_sbufs) vbuf->ops->dbufcon_kunmap(vbuf, num_i_planes); else vbuf->ops->plane_kunmap(vbuf, num_i_planes, 0); if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &vctx->queue.state)) { pos_ext_p = num_i_planes + num_m_planes; for (i = 0; i < NUM_OF_EXT_PLANE; i++) vbuf->ops->plane_kunmap(vbuf, pos_ext_p + i, 0); } } } int is_start_streaming(struct vb2_queue *vbq, unsigned int count) { struct is_video_ctx *ivc = (struct is_video_ctx *)vbq->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; int ret; mdbgv_video(ivc, iv, __func__); if (test_bit(IS_QUEUE_STREAM_ON, &iq->state)) { err("[%s] already streaming", iq->name); return -EINVAL; } if (iq->buf_rdycount && !test_bit(IS_QUEUE_BUFFER_READY, &iq->state)) { err("[%s] need at least %u buffers", iq->name); return -EINVAL; } ret = CALL_QOPS(iq, start_streaming, ivc->device, iq); if (ret) { err("[%s] failed to start_streaming for device: %d", iq->name, ret); return ret; } set_bit(IS_QUEUE_STREAM_ON, &iq->state); return 0; } void is_stop_streaming(struct vb2_queue *vbq) { struct is_video_ctx *ivc = (struct is_video_ctx *)vbq->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; int ret; mdbgv_video(ivc, iv, __func__); /* * If you prepare or queue buffers, and then call streamoff without * ever having called streamon, you would still expect those buffers * to be returned to their normal dequeued state. */ if (!test_bit(IS_QUEUE_STREAM_ON, &iq->state)) warn("[%s] streaming inactive", iq->name); ret = CALL_QOPS(iq, stop_streaming, ivc->device, iq); if (ret) err("[%s] failed to stop_streaming for device: %d", iq->name, ret); clear_bit(IS_QUEUE_STREAM_ON, &iq->state); clear_bit(IS_QUEUE_BUFFER_READY, &iq->state); clear_bit(IS_QUEUE_BUFFER_PREPARED, &iq->state); } static int __is_buf_queue(struct is_queue *iq, struct vb2_buffer *vb) { struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue); struct is_video *iv = ivc->video; struct is_framemgr *framemgr = &iq->framemgr; struct vb2_v4l2_buffer *vb2_v4l2_buf = to_vb2_v4l2_buffer(vb); struct is_vb2_buf *vbuf = vb_to_is_vb2_buf(vb2_v4l2_buf); unsigned int index = vb->index; unsigned int num_i_planes = vb->num_planes - NUM_OF_META_PLANE; unsigned int num_ext_planes = 0, pos_ext_p = 0; struct is_frame *frame; int i; int ret = 0; /* image planes */ if (IS_ENABLED(DMABUF_CONTAINER) && vbuf->num_merged_dbufs) { /* vbuf has been sorted by the order of buffer */ memcpy(iq->buf_dva[index], vbuf->dva, sizeof(dma_addr_t) * vbuf->num_merged_dbufs * num_i_planes); if (vbuf->kva[0]) memcpy(iq->buf_kva[index], vbuf->kva, sizeof(ulong) * vbuf->num_merged_dbufs * num_i_planes); else memset(iq->buf_kva[index], 0x0, sizeof(ulong) * vbuf->num_merged_dbufs * num_i_planes); } else { /* if additional plane exist, map for use ext feature */ if (test_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state)) num_ext_planes = NUM_OF_EXT_PLANE; num_i_planes -= num_ext_planes; for (i = 0; i < num_i_planes; i++) { if (test_bit(IS_QUEUE_NEED_TO_REMAP, &iq->state)) iq->buf_dva[index][i] = vbuf->dva[i]; else iq->buf_dva[index][i] = vbuf->ops->plane_dvaddr(vbuf, i); } if (vbuf->kva[0]) memcpy(iq->buf_kva[index], vbuf->kva, sizeof(ulong) * num_i_planes); else memset(iq->buf_kva[index], 0x0, sizeof(ulong) * num_i_planes); } frame = &framemgr->frames[index]; frame->ext_planes = num_ext_planes; /* ext plane if exist */ if(test_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state)) { pos_ext_p = (num_i_planes * frame->num_buffers) + NUM_OF_META_PLANE; for (i = pos_ext_p; i < pos_ext_p + num_ext_planes; i++) { iq->buf_kva[index][i] = vbuf->ops->plane_kmap(vbuf, i, 0); if (!iq->buf_kva[index][i]) { mverr("failed to get ext kva for %s", ivc, iv, iq->name); ret = -ENOMEM; goto err_get_kva_for_ext; } iq->buf_dva[index][i] = vbuf->ops->plane_dvaddr(vbuf, i); if (!iq->buf_dva[index][i]) { mverr("failed to get ext dva for %s", ivc, iv, iq->name); ret = -ENOMEM; goto err_get_dva_for_ext; } } } if (iv->video_type == IS_VIDEO_TYPE_LEADER) { #if defined(USE_OFFLINE_PROCESSING) for (i = 0; i < num_ext_planes; i++) { frame->shot->uctl.offlineDtDesc.base_kvaddr = iq->buf_kva[index][pos_ext_p + i]; frame->shot->uctl.offlineDtDesc.base_dvaddr = iq->buf_dva[index][pos_ext_p + i]; } #else for (i = 0; i < num_ext_planes; i++) frame->kvaddr_ext[i] = iq->buf_kva[index][pos_ext_p + i]; #endif #ifdef MEASURE_TIME frame->tzone = (struct timespec64 *)frame->shot_ext->timeZone; #endif if (IS_ENABLED(LOGICAL_VIDEO_NODE) && (iq->mode == CAMERA_NODE_LOGICAL)) { ret = _is_queue_subbuf_queue(ivc, iq, frame); if (ret) { mverr("[%s][I%d]Failed to subbuf_queue", ivc, iv, iq->name, index); goto err_logical_node; } } } /* uninitialized frame need to get info */ if (!test_bit(FRAME_MEM_INIT, &frame->mem_state)) goto set_info; /* plane address check */ for (i = 0; i < frame->planes; i++) { if (frame->dvaddr_buffer[i] != iq->buf_dva[index][i]) { if (iv->resourcemgr->hal_version == IS_HAL_VER_3_2) { frame->dvaddr_buffer[i] = iq->buf_dva[index][i]; } else { mverr("buffer[%d][%d] is changed(%pad != %pad)", ivc, iv, index, i, &frame->dvaddr_buffer[i], &iq->buf_dva[index][i]); ret = -EINVAL; goto err_dva_changed; } } if (frame->kvaddr_buffer[i] != iq->buf_kva[index][i]) { if (iv->resourcemgr->hal_version == IS_HAL_VER_3_2) { frame->kvaddr_buffer[i] = iq->buf_kva[index][i]; } else { mverr("kvaddr buffer[%d][%d] is changed(0x%08lx != 0x%08lx)", ivc, iv, index, i, frame->kvaddr_buffer[i], iq->buf_kva[index][i]); ret = -EINVAL; goto err_kva_changed; } } } return 0; set_info: if (test_bit(IS_QUEUE_BUFFER_PREPARED, &iq->state)) { mverr("already prepared but new index(%d) is came", ivc, iv, index); ret = -EINVAL; goto err_queue_prepared_already; } for (i = 0; i < frame->planes; i++) { frame->dvaddr_buffer[i] = iq->buf_dva[index][i]; frame->kvaddr_buffer[i] = iq->buf_kva[index][i]; frame->size[i] = iq->framecfg.size[i]; #ifdef PRINT_BUFADDR mvinfo("%s %d.%d %pad\n", ivc, iv, framemgr->name, index, i, &frame->dvaddr_buffer[i]); #endif } set_bit(FRAME_MEM_INIT, &frame->mem_state); iq->buf_refcount++; if (iq->buf_rdycount == iq->buf_refcount) set_bit(IS_QUEUE_BUFFER_READY, &iq->state); if (iq->buf_maxcount == iq->buf_refcount) { if (IS_ENABLED(DMABUF_CONTAINER) && vbuf->num_merged_dbufs) mvinfo("%s number of merged buffers: %d\n", ivc, iv, iq->name, frame->num_buffers); set_bit(IS_QUEUE_BUFFER_PREPARED, &iq->state); } iq->buf_que++; err_logical_node: err_queue_prepared_already: err_kva_changed: err_dva_changed: err_get_kva_for_ext: err_get_dva_for_ext: return ret; } void is_buf_queue(struct vb2_buffer *vb) { struct is_video_ctx *ivc = (struct is_video_ctx *)vb->vb2_queue->drv_priv; struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; struct is_group *ig = ivc->group; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_groupmgr *grpmgr; u32 instance = get_instance_video_ctx(ivc); int ret; mvdbgs(3, "%s(%d)\n", ivc, iq, __func__, vb->index); ret = __is_buf_queue(iq, vb); if (ret) { mierr("failure in _is_buf_queue(): %d", instance, ret); return; } /* for each leader */ if (iv->video_type == IS_VIDEO_TYPE_LEADER) { if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; if (!test_bit(IS_ISCHAIN_OPEN, &idi->state)) { merr("trying buf_queue to not opened device", idi); return; } grpmgr = idi->groupmgr; mdbgs_ischain(4, "%s\n", idi, __func__); } else { ids = (struct is_device_sensor *)ivc->device; if (!test_bit(IS_SENSOR_OPEN, &ids->state)) { merr("trying buf_queue to not opened device", ids); return; } grpmgr = ids->groupmgr; mdbgs_sensor(4, "%s\n", ids, __func__); } ret = is_group_buffer_queue(grpmgr, ig, iq, vb->index); if (ret) mierr("failure in is_group_buffer_queue(): %d", instance, ret); /* for each non-leader */ } else { ret = is_subdev_buffer_queue(ivc->subdev, vb); if (ret) mierr("failure in is_subdev_buffer_queue(): %d", instance, ret); } } const struct vb2_ops is_default_vb2_ops = { .queue_setup = is_queue_setup, .wait_prepare = is_wait_prepare, .wait_finish = is_wait_finish, .buf_init = is_buf_init, .buf_prepare = is_buf_prepare, .buf_finish = is_buf_finish, .buf_cleanup = is_buf_cleanup, .start_streaming = is_start_streaming, .stop_streaming = is_stop_streaming, .buf_queue = is_buf_queue, }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Pablo v42l file operaions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static enum is_device_type device_type_vid(int vid) { if ((vid >= IS_VIDEO_SS0_NUM && vid <= IS_VIDEO_SS5_NUM) || (vid >= IS_VIDEO_SS0VC0_NUM && vid < IS_VIDEO_SS5VC3_NUM)) return IS_DEVICE_SENSOR; else return IS_DEVICE_ISCHAIN; } static unsigned int rsctype_vid(int vid) { if (vid >= IS_VIDEO_SS0_NUM && vid <= IS_VIDEO_SS5_NUM) return vid - IS_VIDEO_SS0_NUM; else if (vid >= IS_VIDEO_SS0VC0_NUM && vid < IS_VIDEO_SS5VC3_NUM) /* FIXME: it depends on the nubmer of VC channels: 4 */ return (vid - IS_VIDEO_SS0VC0_NUM) >> 2; else return RESOURCE_TYPE_ISCHAIN; } static struct is_video_ctx *is_vctx_open(struct file *file, struct is_video *video, u32 instance, ulong id, const char *name) { struct is_video_ctx *ivc; if (atomic_read(&video->refcount) > IS_STREAM_COUNT) { err("[V%02d] can't open vctx, refcount is invalid", video->id); return ERR_PTR(-EINVAL); } ivc = vzalloc(sizeof(struct is_video_ctx)); if (!ivc) { err("[V%02d] vzalloc is fail", video->id); return ERR_PTR(-ENOMEM); } ivc->refcount = vref_get(video); ivc->instance = instance; ivc->queue.id = id; snprintf(ivc->queue.name, sizeof(ivc->queue.name), "%s", name); ivc->state = BIT(IS_VIDEO_CLOSE); file->private_data = ivc; return ivc; } static int is_vctx_close(struct file *file, struct is_video *video, struct is_video_ctx *vctx) { vfree(vctx); file->private_data = NULL; return vref_put(video, NULL); } static int __is_video_open(struct is_video_ctx *ivc, struct is_video *iv, void *device) { struct is_queue *iq = &ivc->queue; int ret; if (!(ivc->state & BIT(IS_VIDEO_CLOSE))) { mverr("already open(%lX)", ivc, iv, ivc->state); return -EEXIST; } if (atomic_read(&iv->refcount) == 1) { sema_init(&iv->smp_multi_input, 1); iv->try_smp = false; } ivc->device = device; ivc->video = iv; ivc->vops.qbuf = is_video_qbuf; ivc->vops.dqbuf = is_video_dqbuf; ivc->vops.done = is_video_buffer_done; if (iv->device_type == IS_DEVICE_ISCHAIN) { if (iv->video_type == IS_VIDEO_TYPE_LEADER) iq->qops = is_get_ischain_device_qops(); else iq->qops = is_get_ischain_subdev_qops(); } else { if (iv->video_type == IS_VIDEO_TYPE_LEADER) iq->qops = is_get_sensor_device_qops(); else iq->qops = is_get_sensor_subdev_qops(); } ret = is_queue_open(iq, iv->buf_rdycount); if (ret) { mverr("failure in is_queue_open(): %d", ivc, iv, ret); return ret; } iq->vbq = vzalloc(sizeof(struct vb2_queue)); if (!iq->vbq) { mverr("out of memory for vbq", ivc, iv); return -ENOMEM; } ret = queue_init(ivc, iq->vbq, NULL); if (ret) { mverr("failure in queue_init(): %d", ivc, iv, ret); vfree(iq->vbq); iq->vbq = NULL; return ret; } ivc->state = BIT(IS_VIDEO_OPEN); return 0; } static int __is_video_close(struct is_video_ctx *ivc) { struct is_video *iv = ivc->video; struct is_queue *iq = &ivc->queue; if (ivc->state < BIT(IS_VIDEO_OPEN)) { mverr("already close(%lX)", ivc, iv, ivc->state); return -ENOENT; } vb2_queue_release(iq->vbq); vfree(iq->vbq); iq->vbq = NULL; is_queue_close(iq); /* * vb2 release can call stop callback * not if video node is not stream off */ ivc->device = NULL; ivc->state = BIT(IS_VIDEO_CLOSE); return 0; } int is_video_open(struct file *file) { struct is_video *iv = video_drvdata(file); struct is_resourcemgr *rscmgr = iv->resourcemgr; void *device; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_video_ctx *ivc; u32 instance; int ret; ret = is_resource_open(rscmgr, rsctype_vid(iv->id), &device); if (ret) { err("failure in is_resource_open(): %d", ret); goto err_resource_open; } if (!device) { err("failed to get device"); ret = -EINVAL; goto err_invalid_device; } if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)device; instance = idi->instance; minfo("[%s]%s\n", idi, vn_name[iv->id], __func__); } else { ids = (struct is_device_sensor *)device; instance = ids->instance; if (ids->reboot && iv->video_type == IS_VIDEO_TYPE_LEADER) { warn("%s%s: failed to open sensor due to reboot", vn_name[iv->id], __func__); ret = -EINVAL; goto err_reboot; } minfo("[%s]%s\n", ids, vn_name[iv->id], __func__); } ivc = is_vctx_open(file, iv, instance, iv->subdev_id, vn_name[iv->id]); if (IS_ERR_OR_NULL(ivc)) { ret = PTR_ERR(ivc); err("failure in open_vctx(): %d", instance, ret); goto err_vctx_open; } ret = __is_video_open(ivc, iv, device); if (ret) { mierr("failure in __is_video_open(): %d", instance, ret); goto err_video_open; } if (iv->video_type == IS_VIDEO_TYPE_LEADER) { ivc->group = (struct is_group *)((char *)device + iv->group_ofs); if (iv->device_type == IS_DEVICE_ISCHAIN) { ret = is_ischain_group_open(idi, ivc, iv->group_id); if (ret) { mierr("failure in is_ischain_group_open(): %d", instance, ret); goto err_ischain_group_open; } } else { ret = is_sensor_open(ids, ivc); if (ret) { mierr("failure in is_sensor_open(): %d", instance, ret); goto err_sensor_open; } } } else { ivc->subdev = (struct is_subdev *)((char *)device + iv->subdev_ofs); if (iv->device_type == IS_DEVICE_ISCHAIN) { ret = is_ischain_subdev_open(idi, ivc); if (ret) { mierr("failure in is_ischain_subdev_open(): %d", instance, ret); goto err_ischain_subdev_open; } } else { ret = is_sensor_subdev_open(ids, ivc); if (ret) { mierr("failure in is_sensor_subdev_open(): %d", instance, ret); goto err_sensor_subdev_open; } } } return 0; err_sensor_subdev_open: err_ischain_subdev_open: err_sensor_open: err_ischain_group_open: __is_video_close(ivc); err_video_open: is_vctx_close(file, iv, ivc); err_vctx_open: err_reboot: err_invalid_device: err_resource_open: return ret; } int is_video_close(struct file *file) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance; struct is_device_ischain *idi; struct is_device_sensor *ids; int refcount; int ret; if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; instance = idi->instance; } else { ids = (struct is_device_sensor *)ivc->device; instance = ids->instance; } if (iv->video_type == IS_VIDEO_TYPE_LEADER) { if (iv->device_type == IS_DEVICE_ISCHAIN) { ret = is_ischain_group_close(idi, ivc, ivc->group); if (ret) mierr("failure in is_ischain_group_close(): %d", instance, ret); } else { ret = is_sensor_close(ids); if (ret) mierr("failure in is_sensor_close(): %d", instance, ret); } } else { if (iv->device_type == IS_DEVICE_ISCHAIN) { ret = is_ischain_subdev_close(idi, ivc); if (ret) mierr("failure in is_ischain_subdev_close(): %d", instance, ret); } else { ret = is_sensor_subdev_close(ids, ivc); if (ret) mierr("failure in is_sensor_subdev_close(): %d", instance, ret); } } ret = __is_video_close(ivc); if (ret) mierr("failure in is_video_close(): %d", instance, ret); refcount = is_vctx_close(file, iv, ivc); if (refcount < 0) mierr("failure in is_vctx_close(): %d", instance, refcount); if (iv->device_type == IS_DEVICE_ISCHAIN) minfo("[%s]%s(open count: %d, ref. cont: %d): %d\n", idi, vn_name[iv->id], __func__, atomic_read(&idi->open_cnt), refcount, ret); else minfo("[%s]%s(ref. cont: %d): %d\n", ids, vn_name[iv->id], __func__, refcount, ret); return 0; } __poll_t is_video_poll(struct file *file, struct poll_table_struct *wait) { struct is_video_ctx *vctx = file->private_data; struct is_queue *queue = GET_QUEUE(vctx); return vb2_poll(queue->vbq, file, wait); } int is_video_mmap(struct file *file, struct vm_area_struct *vma) { struct is_video_ctx *vctx = file->private_data; struct is_queue *queue = GET_QUEUE(vctx); return vb2_mmap(queue->vbq, vma); } static struct v4l2_file_operations is_default_v4l2_file_ops = { .owner = THIS_MODULE, .open = is_video_open, .release = is_video_close, .poll = is_video_poll, .unlocked_ioctl = video_ioctl2, .mmap = is_video_mmap, }; static struct v4l2_ioctl_ops is_default_v4l2_ioctl_ops; int is_video_probe(struct is_video *video, char *video_name, u32 video_number, u32 vfl_dir, struct is_mem *mem, struct v4l2_device *v4l2_dev, const struct v4l2_file_operations *file_ops, const struct v4l2_ioctl_ops *ioctl_ops, const struct vb2_ops *vb2_ops) { int ret = 0; u32 video_id; vref_init(video); mutex_init(&video->lock); snprintf(video->vd.name, sizeof(video->vd.name), "%s", video_name); video->id = video_number; video->vb2_ops = vb2_ops ? vb2_ops : &is_default_vb2_ops; video->vb2_mem_ops = mem->vb2_mem_ops; video->is_vb2_buf_ops = mem->is_vb2_buf_ops; video->alloc_ctx = mem->priv; video->alloc_dev = mem->dev; video->video_type = (vfl_dir == VFL_DIR_RX) ? IS_VIDEO_TYPE_CAPTURE : IS_VIDEO_TYPE_LEADER; video->device_type = device_type_vid(video_number); video->vd.vfl_dir = vfl_dir; video->vd.v4l2_dev = v4l2_dev; video->vd.fops = file_ops ? file_ops : &is_default_v4l2_file_ops; video->vd.ioctl_ops = ioctl_ops ? ioctl_ops : &is_default_v4l2_ioctl_ops; video->vd.minor = -1; video->vd.release = video_device_release; video->vd.lock = &video->lock; video->vd.device_caps = (vfl_dir == VFL_DIR_RX) ? VIDEO_CAPTURE_DEVICE_CAPS : VIDEO_OUTPUT_DEVICE_CAPS; video_set_drvdata(&video->vd, video); video_id = EXYNOS_VIDEONODE_FIMC_IS + video_number; ret = video_register_device(&video->vd, VFL_TYPE_PABLO, video_id); if (ret) { err("[V%02d] Failed to register video device", video->id); goto p_err; } p_err: info("[VID] %s(%d) is created. minor(%d)\n", video_name, video_id, video->vd.minor); return ret; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Pablo v42l ioctl operaions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int is_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct is_video *iv = video_drvdata(file); snprintf(cap->driver, sizeof(cap->driver), "%s", iv->vd.name); snprintf(cap->card, sizeof(cap->card), "%s", iv->vd.name); if (iv->video_type == IS_VIDEO_TYPE_LEADER) cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE; else cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE; cap->device_caps |= cap->capabilities; return 0; } int is_vidioc_g_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { return 0; } int is_vidioc_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; int ret; u32 condition; mdbgv_video(ivc, iv, __func__); /* capture video node can skip s_input */ if (iv->video_type == IS_VIDEO_TYPE_LEADER) condition = BIT(IS_VIDEO_S_INPUT) | BIT(IS_VIDEO_S_FORMAT) | BIT(IS_VIDEO_S_BUFS); else condition = BIT(IS_VIDEO_S_INPUT) | BIT(IS_VIDEO_S_BUFS) | BIT(IS_VIDEO_S_FORMAT) | BIT(IS_VIDEO_OPEN) | BIT(IS_VIDEO_STOP); if (!(ivc->state & condition)) { mverr("invalid state(%lX)", ivc, iv, ivc->state); return -EINVAL; } ret = is_queue_set_format_mplane(ivc, iq, ivc->device, f); if (ret) { mverr("failure in is_queue_set_format_mplane(): %d", ivc, iv, ret); return ret; } ivc->state = BIT(IS_VIDEO_S_FORMAT); mdbgv_vid("set_format(%d x %d)\n", iq->framecfg.width, iq->framecfg.height); return 0; } int is_vidioc_reqbufs(struct file *file, void* fh, struct v4l2_requestbuffers *b) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_subdev *leader; struct is_queue *iq = &ivc->queue; struct is_framemgr *framemgr = &iq->framemgr; int ret; if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; mdbgv_ischain("%s(buffer count: %d\n", ivc, __func__, b->count); } else { ids = (struct is_device_sensor *)ivc->device; mdbgv_sensor("%s(buffer count: %d)\n", ivc, __func__, b->count); } if (iv->video_type != IS_VIDEO_TYPE_LEADER) { if (iv->device_type == IS_DEVICE_ISCHAIN) { leader = ivc->subdev->leader; if (leader && test_bit(IS_SUBDEV_START, &leader->state)) { merr("leader%d still running, subdev%d req is not applied", idi, leader->id, ivc->subdev->id); return -EBUSY; } } else { if (test_bit(IS_SENSOR_BACK_START, &ids->state)) { err("sensor%d still running, vid%d req is not applied", ids->device_id, iv->id); return -EBUSY; } } } if (!(ivc->state & (BIT(IS_VIDEO_S_FORMAT) | BIT(IS_VIDEO_STOP) | BIT(IS_VIDEO_S_BUFS)))) { mverr("invalid state(%lX)", ivc, iv, ivc->state); return -EINVAL; } if (test_bit(IS_QUEUE_STREAM_ON, &iq->state)) { mverr("already streaming", ivc, iv); return -EINVAL; } /* before call queue ops if request count is zero */ if (!b->count) { ret = CALL_QOPS(iq, reqbufs, ivc->device, iq, b->count); if (ret) { mverr("failure in reqbufs QOP", ivc, iv, ret); return ret; } } ret = vb2_reqbufs(iq->vbq, b); if (ret) { mverr("failure in vb2_reqbufs(): %d", ivc, iv, ret); return ret; } iq->buf_maxcount = b->count; if (iq->buf_maxcount == 0) { iq->buf_req = 0; iq->buf_pre = 0; iq->buf_que = 0; iq->buf_com = 0; iq->buf_dqe = 0; iq->buf_refcount = 0; clear_bit(IS_QUEUE_BUFFER_READY, &iq->state); clear_bit(IS_QUEUE_BUFFER_PREPARED, &iq->state); frame_manager_close(framemgr); } else { if (iq->buf_maxcount < iq->buf_rdycount) { mverr("buffer count is not invalid(%d < %d)", ivc, iv, iq->buf_maxcount, iq->buf_rdycount); return -EINVAL; } ret = frame_manager_open(framemgr, iq->buf_maxcount); if (ret) { mverr("failure in frame_manager_open(): %d", ivc, iv, ret); return ret; } } /* after call queue ops if request count is not zero */ if (b->count) { ret = CALL_QOPS(iq, reqbufs, ivc->device, iq, b->count); if (ret) { mverr("failure in vb2_reqbufs(): %d", ivc, iv, ret); return ret; } } ivc->state = BIT(IS_VIDEO_S_BUFS); return 0; } int is_vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; int ret; mdbgv_video(ivc, iv, __func__); ret = vb2_querybuf(iq->vbq, b); if (ret) { mverr("failure in vb2_querybuf(): %d", ivc, iv, ret); return ret; } return 0; } int is_vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance = get_instance_video_ctx(ivc); int ret; mvdbgs(3, "%s(%02d:%d)\n", ivc, &ivc->queue, __func__, b->type, b->index); /* FIXME: clh, isp, lme, mcs, vra */ /* struct is_queue *iq = &ivc->queue; if (!test_bit(IS_QUEUE_STREAM_ON, &iq->state)) { mierr("stream off state, can NOT qbuf", instance); return = -EINVAL; } */ ret = CALL_VOPS(ivc, qbuf, b); if (ret) { mierr("failure in qbuf video op: %d", instance, ret); return ret; } return 0; } int is_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance = get_instance_video_ctx(ivc); bool nonblocking = file->f_flags & O_NONBLOCK; int ret; mvdbgs(3, "%s(%02d:%d)\n", ivc, &ivc->queue, __func__, b->type, b->index); ret = CALL_VOPS(ivc, dqbuf, b, nonblocking); if (ret) { if (ret != -EAGAIN) mierr("failure in dqbuf video op: %d", instance, ret); return ret; } return 0; } int is_vidioc_prepare_buf(struct file *file, void *fh, struct v4l2_buffer *b) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; struct vb2_queue *vbq = iq->vbq; struct vb2_buffer *vb; int ret; if (!vbq) { mverr("vbq is NULL", ivc, iv); return -EINVAL; } ret = is_vb2_prepare_buf(vbq, b); if (ret) { mverr("failure in vb2_prepare_buf(index: %d): %d", ivc, iv, b->index, ret); return ret; } vb = vbq->bufs[b->index]; if (!vb) { mverr("vb is NULL", ivc, iv); return -EINVAL; } ret = __is_buf_queue(iq, vb); if (ret) { mverr("failure in __is_buf_queue(index: %d): %d", ivc, iv, b->index, ret); return ret; } info("[%s]%s(index: %d)\n", vn_name[iv->id], __func__, b->index); return 0; } int is_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; struct vb2_queue *vbq = iq->vbq; int ret; mdbgv_video(ivc, iv, __func__); if (!(ivc->state & (BIT(IS_VIDEO_S_BUFS) | BIT(IS_VIDEO_STOP)))) { mverr("invalid state(%lX)", ivc, iv, ivc->state); return -EINVAL; } if (!vbq) { mverr("vbq is NULL", ivc, iv); return -EINVAL; } ret = vb2_streamon(vbq, i); if (ret) { mverr("failure in vb2_streamon(): %d", ivc, iv, ret); return ret; } ivc->state = BIT(IS_VIDEO_START); return 0; } int is_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; struct vb2_queue *vbq = iq->vbq; struct is_framemgr *framemgr = &iq->framemgr; u32 rcnt, pcnt, ccnt; int ret; mdbgv_video(ivc, iv, __func__); if (!(ivc->state & BIT(IS_VIDEO_START))) { mverr("invalid state(%lX)", ivc, iv, ivc->state); return -EINVAL; } framemgr_e_barrier_irq(framemgr, FMGR_IDX_0); rcnt = framemgr->queued_count[FS_REQUEST]; pcnt = framemgr->queued_count[FS_PROCESS]; ccnt = framemgr->queued_count[FS_COMPLETE]; framemgr_x_barrier_irq(framemgr, FMGR_IDX_0); if (rcnt + pcnt + ccnt > 0) mvwarn("streamoff: queued buffer is not empty(R%d, P%d, C%d)", ivc, iv, rcnt, pcnt, ccnt); if (!vbq) { mverr("vbq is NULL", ivc, iv); return -EINVAL; } ret = vb2_streamoff(vbq, i); if (ret) { mverr("failure in vb2_streamoff(): %d", ivc, iv, ret); return ret; } ret = frame_manager_flush(framemgr); if (ret) { mverr("failure in frame_manager_flush(): %d", ivc, iv, ret); return ret; } ivc->state = BIT(IS_VIDEO_STOP); return 0; } int is_vidioc_s_input(struct file *file, void *fs, unsigned int i) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance = get_instance_video_ctx(ivc); u32 scenario, stream, position, vindex, intype, leader; int ret; /* for each leader */ if (iv->video_type == IS_VIDEO_TYPE_LEADER) { scenario = (i & SENSOR_SCN_MASK) >> SENSOR_SCN_SHIFT; stream = (i & INPUT_STREAM_MASK) >> INPUT_STREAM_SHIFT; position = (i & INPUT_POSITION_MASK) >> INPUT_POSITION_SHIFT; vindex = (i & INPUT_VINDEX_MASK) >> INPUT_VINDEX_SHIFT; intype = (i & INPUT_INTYPE_MASK) >> INPUT_INTYPE_SHIFT; leader = (i & INPUT_LEADER_MASK) >> INPUT_LEADER_SHIFT; if (iv->device_type == IS_DEVICE_ISCHAIN) { mdbgv_ischain("%s(input: %08X) - stream: %d, position: %d, vindex: %d, " "intype: %d, leader: %d\n", ivc, __func__, i, stream, position, vindex, intype, leader); ret = is_ischain_group_s_input(ivc->device, ivc->group, stream, position, vindex, intype, leader); if (ret) { mierr("failure in is_ischain_group_s_input(): %d", instance, ret); return ret; } } else { #ifdef CONFIG_USE_SENSOR_GROUP mdbgv_sensor("%s(input: %08X) - scenario: %d, stream: %d, position: %d, " "vindex: %d, intype: %d, leader: %d\n", ivc, __func__, scenario, i, stream, position, vindex, intype, leader); ret = is_sensor_s_input(ivc->device, position, scenario, vindex, leader); if (ret) { mierr("failure in is_sensor_s_input(): %d", instance, ret); return ret; } #else scenario = (i & SENSOR_SCENARIO_MASK) >> SENSOR_SCENARIO_SHIFT; int input = (i & SENSOR_MODULE_MASK) >> SENSOR_MODULE_SHIFT; mdbgv_sensor("%s(input: %08X)\n", __func__, i); ret = is_sensor_s_input(device, input, scenario); if (ret) { mierr("failure in is_sensor_s_input(): %d", instance, ret); return ret; } #endif } } if (!(ivc->state & (BIT(IS_VIDEO_OPEN) | BIT(IS_VIDEO_S_INPUT) | BIT(IS_VIDEO_S_BUFS)))) { mverr("invalid state(%lX)", ivc, iv, ivc->state); return -EINVAL; } ivc->state = BIT(IS_VIDEO_S_INPUT); return 0; } static int is_g_ctrl_completes(struct file *file) { struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; struct is_framemgr *framemgr = &iq->framemgr; return framemgr->queued_count[FS_COMPLETE]; } int is_vidioc_g_ctrl(struct file *file, void * fh, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance = get_instance_video_ctx(ivc); mdbgv_video(ivc, iv, __func__); switch (a->id) { case V4L2_CID_IS_G_COMPLETES: if (iv->video_type != IS_VIDEO_TYPE_LEADER) { a->value = is_g_ctrl_completes(file); } else { mierr("unsupported ioctl(%lx, %d)", instance, a->id, a->id & 0xff); return -EINVAL; } break; default: mierr("unsupported ioctl(%lx, %d)", instance, a->id, a->id & 0xff); return -EINVAL; } return 0; } static int is_g_ctrl_capture_meta(struct file *file, struct v4l2_ext_control *ext_ctrl) { #if IS_ENABLED(USE_DDK_INTF_CAP_META) struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_group *group = ivc->group; struct is_device_ischain *device = GET_DEVICE(ivc); struct is_cap_meta_info cap_meta_info; struct dma_buf *dma_buf = NULL; u32 size, fcount; ulong meta_kva = 0, cap_shot_kva; int dma_buf_fd, ret; mdbgv_video(ivc, iv, __func__); ret = copy_from_user(&cap_meta_info, ext_ctrl->ptr, sizeof(struct is_cap_meta_info)); if (ret) { err("fail to copy_from_user, ret(%d)\n", ret); goto p_err; } fcount = cap_meta_info.frame_count; dma_buf_fd = cap_meta_info.dma_buf_fd; size = (sizeof(u32) * CAMERA2_MAX_IPC_VENDER_LENGTH); dma_buf = dma_buf_get(dma_buf_fd); if (IS_ERR_OR_NULL(dma_buf)) { err("Failed to get dmabuf. fd %d ret %ld", dma_buf_fd, PTR_ERR(dma_buf)); ret = -EINVAL; goto err_get_dbuf; } meta_kva = (ulong)dma_buf_vmap(dma_buf); if (IS_ERR_OR_NULL((void *)meta_kva)) { err("Failed to get kva %ld", meta_kva); ret = -ENOMEM; goto err_vmap; } /* To start the end of camera2_shot_ext */ cap_shot_kva = meta_kva + sizeof(struct camera2_shot_ext); mdbgv_ischain("%s: request capture meta update. fcount(%d), size(%d), addr(%llx)\n", ivc, __func__, fcount, size, cap_shot_kva); ret = is_ischain_g_ddk_capture_meta_update(group, device, fcount, size, (ulong)cap_shot_kva); if (ret) { err("fail to ischain_g_ddk_capture_meta_upadte, ret(%d)\n", ret); goto p_err; } p_err: if (!IS_ERR_OR_NULL((void *) dma_buf)) { dma_buf_vunmap(dma_buf, (void *)meta_kva); meta_kva = 0; } err_vmap: if (!IS_ERR_OR_NULL(dma_buf)) dma_buf_put(dma_buf); err_get_dbuf: #endif return 0; } int is_vidioc_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *device; struct is_framemgr *framemgr; struct is_queue *queue; struct v4l2_ext_control *ext_ctrl; struct v4l2_control ctrl; int i, instance, ret; mdbgv_video(ivc, iv, __func__); device = GET_DEVICE(ivc); queue = GET_QUEUE(ivc); framemgr = &queue->framemgr; instance = get_instance_video_ctx(ivc); if (a->which != V4L2_CTRL_CLASS_CAMERA) { mierr("invalid control class(%d)", instance, a->which); return -EINVAL; } for (i = 0; i < a->count; i++) { ext_ctrl = (a->controls + i); switch (ext_ctrl->id) { case V4L2_CID_IS_G_CAP_META_UPDATE: is_g_ctrl_capture_meta(file, ext_ctrl); break; default: ctrl.id = ext_ctrl->id; ctrl.value = ext_ctrl->value; ret = is_vidioc_g_ctrl(file, fh, &ctrl); if (ret) { merr("is_vidioc_g_ctrl is fail(%d)", device, ret); goto p_err; } break; } } p_err: return 0; } static void is_s_ctrl_flip(struct file *file, struct v4l2_control *a) { struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; unsigned int flipnr; if (a->id == V4L2_CID_HFLIP) flipnr = SCALER_FLIP_COMMAND_X_MIRROR; else flipnr = SCALER_FLIP_COMMAND_Y_MIRROR; if (a->value) set_bit(flipnr, &iq->framecfg.flip); else clear_bit(flipnr, &iq->framecfg.flip); } static int is_s_ctrl_dvfs_cluster(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_resourcemgr *rscmgr; if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; rscmgr = idi->resourcemgr; } else { ids = (struct is_device_sensor *)ivc->device; rscmgr = ids->resourcemgr; } return is_resource_ioctl(rscmgr, a); } static int is_s_ctrl_setfile(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi = GET_DEVICE_ISCHAIN(ivc); u32 scenario; if (test_bit(IS_ISCHAIN_START, &idi->state)) { mverr("already start, failed to apply setfile", ivc, iv); return -EINVAL; } idi->setfile = a->value; scenario = (idi->setfile & IS_SCENARIO_MASK) >> IS_SCENARIO_SHIFT; mvinfo("[S_CTRL] setfile(%d), scenario(%d)\n", idi, iv, idi->setfile & IS_SETFILE_MASK, scenario); return 0; } static int is_s_ctrl_camera_type(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi = (struct is_device_ischain *)ivc->device; if (a->value == IS_COLD_BOOT) { /* change value to X when !TWIZ | front */ is_itf_fwboot_init(idi->interface); } else if (a->value == IS_WARM_BOOT) { /* change value to X when TWIZ & back | frist time back camera */ if (!test_bit(IS_IF_LAUNCH_FIRST, &idi->interface->launch_state)) idi->interface->fw_boot_mode = FIRST_LAUNCHING; else idi->interface->fw_boot_mode = WARM_BOOT; } else { mverr("invalid camera type", ivc, iv, a->id); return -EINVAL; } return 0; } static void is_s_ctrl_dvfs_scenario(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi = (struct is_device_ischain *)ivc->device; struct is_dvfs_ctrl *dvfs_ctrl = &idi->resourcemgr->dvfs_ctrl; u32 vendor; dvfs_ctrl->dvfs_scenario = a->value; vendor = (dvfs_ctrl->dvfs_scenario >> IS_DVFS_SCENARIO_VENDOR_SHIFT) & IS_DVFS_SCENARIO_VENDOR_MASK; mvinfo("[S_CTRL][DVFS] value(%x), common(%d), vendor(%d)\n", ivc, iv, dvfs_ctrl->dvfs_scenario, dvfs_ctrl->dvfs_scenario & IS_DVFS_SCENARIO_COMMON_MODE_MASK, vendor); } static void is_s_ctrl_dvfs_recording_size(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi = (struct is_device_ischain *)ivc->device; struct is_dvfs_ctrl *dvfs_ctrl = &idi->resourcemgr->dvfs_ctrl; dvfs_ctrl->dvfs_rec_size = a->value; mvinfo("[S_CTRL][DVFS] rec_width(%d), rec_height(%d)\n", ivc, iv, dvfs_ctrl->dvfs_rec_size & 0xffff, dvfs_ctrl->dvfs_rec_size >> IS_DVFS_SCENARIO_HEIGHT_SHIFT); } static int is_s_ctrl_hal_version(struct file *file, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_device_ischain *idi; struct is_device_sensor *ids; struct is_resourcemgr *rscmgr; if (iv->device_type == IS_DEVICE_ISCHAIN) { idi = (struct is_device_ischain *)ivc->device; rscmgr = idi->resourcemgr; } else { ids = (struct is_device_sensor *)ivc->device; rscmgr = ids->resourcemgr; } if (a->value < IS_HAL_VER_1_0 || a->value >= IS_HAL_VER_MAX) { mverr("hal version(%d) is invalid", ivc, iv, a->value); return -EINVAL; } rscmgr->hal_version = a->value; return 0; } int is_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; struct is_queue *iq = &ivc->queue; struct is_device_ischain *idi; struct is_core *core; u32 instance = get_instance_video_ctx(ivc); int ret; mdbgv_video(ivc, iv, __func__); switch (a->id) { case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: is_s_ctrl_flip(file, a); break; case V4L2_CID_IS_G_CAPABILITY: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) is_ischain_g_capability(idi, a->value); else return -EINVAL; break; case V4L2_CID_IS_DVFS_LOCK: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) { is_pm_qos_add_request(&idi->user_qos, PM_QOS_DEVICE_THROUGHPUT, a->value); mvinfo("[S_CTRL][DVFS] lock: %d\n", ivc, iv, a->value); } else { return -EINVAL; } break; case V4L2_CID_IS_DVFS_UNLOCK: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) { is_pm_qos_remove_request(&idi->user_qos); mvinfo("[S_CTRL][DVFS] unlock: %d\n", ivc, iv, a->value); } else { return -EINVAL; } break; case V4L2_CID_IS_DVFS_CLUSTER0: case V4L2_CID_IS_DVFS_CLUSTER1: case V4L2_CID_IS_DVFS_CLUSTER2: return is_s_ctrl_dvfs_cluster(file, a); case V4L2_CID_IS_S_USE_EXT_PLANE: if (a->value) set_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state); else clear_bit(IS_QUEUE_NEED_TO_EXTMAP, &iq->state); mvinfo("[S_CTRL][%s] use extra plane: %d\n", ivc, iv, iq->name, a->value); break; case V4L2_CID_IS_FORCE_DONE: if (iv->video_type == IS_VIDEO_TYPE_LEADER) { set_bit(IS_GROUP_REQUEST_FSTOP, &ivc->group->state); } else { mierr("unsupported ioctl(%lx, %d)", instance, a->id, a->id & 0xff); return -EINVAL; } break; case V4L2_CID_IS_SET_SETFILE: return is_s_ctrl_setfile(file, a); case V4L2_CID_IS_END_OF_STREAM: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) return is_ischain_open_wrap(idi, true); else return -EINVAL; case V4L2_CID_IS_CAMERA_TYPE: if (iv->device_type == IS_DEVICE_ISCHAIN) return is_s_ctrl_camera_type(file, a); else return -EINVAL; case V4L2_CID_IS_S_DVFS_SCENARIO: if (iv->device_type == IS_DEVICE_ISCHAIN) is_s_ctrl_dvfs_scenario(file, a); else return -EINVAL; break; case V4L2_CID_IS_S_DVFS_RECORDING_SIZE: if (iv->device_type == IS_DEVICE_ISCHAIN) is_s_ctrl_dvfs_recording_size(file, a); else return -EINVAL; break; case V4L2_CID_IS_OPENING_HINT: core = is_get_is_core(); if (core) { mvinfo("opening hint(%d)\n", ivc, iv, a->value); core->vender.opening_hint = a->value; } break; case V4L2_CID_IS_CLOSING_HINT: core = is_get_is_core(); if (core) { mvinfo("closing hint(%d)\n", ivc, iv, a->value); core->vender.closing_hint = a->value; } break; case V4L2_CID_IS_MAX_SIZE: core = is_get_is_core(); if (core) { core->vender.isp_max_width = a->value >> 16; core->vender.isp_max_height = a->value & 0xffff; mvinfo("[S_CTRL] max buffer size: %d x %d (0x%08X)", ivc, iv, core->vender.isp_max_width, core->vender.isp_max_height, a->value); } break; case V4L2_CID_IS_YUV_MAX_SIZE: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) { idi->yuv_max_width = a->value >> 16; idi->yuv_max_height = a->value & 0xffff; mvinfo("[S_CTRL] max yuv buffer size: %d x %d (0x%08X)", ivc, iv, idi->yuv_max_width, idi->yuv_max_height, a->value); } break; case V4L2_CID_IS_DEBUG_DUMP: info("camera resource dump is requested\n"); is_resource_dump(); if (a->value) panic("and requested panic from user"); break; case V4L2_CID_IS_DEBUG_SYNC_LOG: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) return is_logsync(idi->interface, a->value, IS_MSG_TEST_SYNC_LOG); else return -EINVAL; case V4L2_CID_IS_HAL_VERSION: return is_s_ctrl_hal_version(file, a); case V4L2_CID_IS_S_LLC_CONFIG: idi = GET_DEVICE_ISCHAIN(ivc); if (idi) idi->llc_mode = a->value; else return -EINVAL; break; default: ret = is_vender_vidioc_s_ctrl(ivc, a); if (ret) { mierr("unsupported ioctl(%lx, %d)", instance, a->id, a->id & 0xff); return -EINVAL; } } return 0; } int is_vidioc_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a) { struct is_video *iv = video_drvdata(file); struct is_video_ctx *ivc = (struct is_video_ctx *)file->private_data; u32 instance = get_instance_video_ctx(ivc); struct v4l2_ext_control *ext_ctrl; struct v4l2_control ctrl; int i, ret; mdbgv_video(ivc, iv, __func__); if (a->which != V4L2_CTRL_CLASS_CAMERA) { mierr("invalid control class(%d)", instance, a->which); return -EINVAL; } for (i = 0; i < a->count; i++) { ext_ctrl = (a->controls + i); ctrl.id = ext_ctrl->id; ctrl.value = ext_ctrl->value; ret = is_vidioc_s_ctrl(file, fh, &ctrl); if (ret) { mierr("failure in is_vidioc_s_ctrl(): %d", instance, ret); return ret; } } return 0; } static struct v4l2_ioctl_ops is_default_v4l2_ioctl_ops = { .vidioc_querycap = is_vidioc_querycap, .vidioc_g_fmt_vid_out_mplane = is_vidioc_g_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = is_vidioc_g_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = is_vidioc_s_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = is_vidioc_s_fmt_mplane, .vidioc_reqbufs = is_vidioc_reqbufs, .vidioc_querybuf = is_vidioc_querybuf, .vidioc_qbuf = is_vidioc_qbuf, .vidioc_dqbuf = is_vidioc_dqbuf, .vidioc_prepare_buf = is_vidioc_prepare_buf, .vidioc_streamon = is_vidioc_streamon, .vidioc_streamoff = is_vidioc_streamoff, .vidioc_s_input = is_vidioc_s_input, .vidioc_g_ctrl = is_vidioc_g_ctrl, .vidioc_g_ext_ctrls = is_vidioc_g_ext_ctrls, .vidioc_s_ctrl = is_vidioc_s_ctrl, .vidioc_s_ext_ctrls = is_vidioc_s_ext_ctrls, }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Pablo video operaions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int is_video_qbuf(struct is_video_ctx *vctx, struct v4l2_buffer *buf) { int ret = 0; struct is_queue *queue; struct vb2_queue *vbq; struct vb2_buffer *vb; struct is_video *video; int plane; struct vb2_plane planes[VB2_MAX_PLANES]; FIMC_BUG(!vctx); FIMC_BUG(!buf); TIME_QUEUE(TMQ_QS); queue = GET_QUEUE(vctx); vbq = queue->vbq; video = GET_VIDEO(vctx); if (!vbq) { mverr("vbq is NULL", vctx, video); ret = -EINVAL; goto p_err; } queue->buf_req++; ret = is_vb2_qbuf(queue->vbq, buf); if (ret) { mverr("vb2_qbuf is fail(index : %d, %d)", vctx, video, buf->index, ret); if (vbq->fileio) { mverr("file io in progress", vctx, video); ret = -EBUSY; goto p_err; } if (buf->type != queue->vbq->type) { mverr("buf type is invalid(%d != %d)", vctx, video, buf->type, queue->vbq->type); ret = -EINVAL; goto p_err; } if (buf->index >= vbq->num_buffers) { mverr("buffer index%d out of range", vctx, video, buf->index); ret = -EINVAL; goto p_err; } if (buf->memory != vbq->memory) { mverr("invalid memory type%d", vctx, video, buf->memory); ret = -EINVAL; goto p_err; } vb = vbq->bufs[buf->index]; if (!vb) { mverr("vb is NULL", vctx, video); ret = -EINVAL; goto p_err; } if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { /* Is memory for copying plane information present? */ if (buf->m.planes == NULL) { mverr("multi-planar buffer passed but " "planes array not provided\n", vctx, video); ret = -EINVAL; goto p_err; } if (buf->length < vb->num_planes || buf->length > VB2_MAX_PLANES) { mverr("incorrect planes array length, " "expected %d, got %d\n", vctx, video, vb->num_planes, buf->length); ret = -EINVAL; goto p_err; } } /* for detect vb2 framework err, operate some vb2 functions */ memset(planes, 0, sizeof(planes[0]) * vb->num_planes); vb->vb2_queue->buf_ops->is_fill_vb2_buffer(vb, buf, planes); for (plane = 0; plane < vb->num_planes; ++plane) { struct dma_buf *dbuf; dbuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR_OR_NULL(dbuf)) { mverr("invalid dmabuf fd(%d) for plane %d\n", vctx, video, planes[plane].m.fd, plane); goto p_err; } if (planes[plane].length == 0) planes[plane].length = (unsigned int)dbuf->size; if (planes[plane].length < vb->planes[plane].min_length) { mverr("invalid dmabuf length %u for plane %d, " "minimum length %u\n", vctx, video, planes[plane].length, plane, vb->planes[plane].min_length); dma_buf_put(dbuf); goto p_err; } dma_buf_put(dbuf); } goto p_err; } p_err: TIME_QUEUE(TMQ_QE); return ret; } int is_video_dqbuf(struct is_video_ctx *vctx, struct v4l2_buffer *buf, bool blocking) { int ret = 0; struct is_video *video; struct is_queue *queue; struct is_sysfs_debug *sysfs_debug; FIMC_BUG(!vctx); FIMC_BUG(!GET_VIDEO(vctx)); FIMC_BUG(!buf); video = GET_VIDEO(vctx); queue = GET_QUEUE(vctx); sysfs_debug = is_get_sysfs_debug(); if (!queue->vbq) { mverr("vbq is NULL", vctx, video); ret = -EINVAL; goto p_err; } if (buf->type != queue->vbq->type) { mverr("buf type is invalid(%d != %d)", vctx, video, buf->type, queue->vbq->type); ret = -EINVAL; goto p_err; } if (!test_bit(IS_QUEUE_STREAM_ON, &queue->state)) { mverr("queue is not streamon(%ld)", vctx, video, queue->state); ret = -EINVAL; goto p_err; } queue->buf_dqe++; ret = vb2_dqbuf(queue->vbq, buf, blocking); if (ret) { mverr("vb2_dqbuf is fail(%d)", vctx, video, ret); if (test_bit(IS_HAL_DEBUG_SUDDEN_DEAD_DETECT, &sysfs_debug->hal_debug_mode) && ret == -ERESTARTSYS) { msleep(sysfs_debug->hal_debug_delay); panic("HAL dead"); } goto p_err; } p_err: TIME_QUEUE(TMQ_DQ); return ret; } int is_video_buffer_done(struct is_video_ctx *vctx, u32 index, u32 state) { int ret = 0; struct vb2_buffer *vb; struct is_queue *queue; struct is_video *video; FIMC_BUG(!vctx); FIMC_BUG(!vctx->video); FIMC_BUG(index >= IS_MAX_BUFS); queue = GET_QUEUE(vctx); video = GET_VIDEO(vctx); if (!queue->vbq) { mverr("vbq is NULL", vctx, video); ret = -EINVAL; goto p_err; } vb = queue->vbq->bufs[index]; if (!vb) { mverr("vb is NULL", vctx, video); ret = -EINVAL; goto p_err; } if (!test_bit(IS_QUEUE_STREAM_ON, &queue->state)) { warn("%d video queue is not stream on", vctx->video->id); ret = -EINVAL; goto p_err; } if (vb->state != VB2_BUF_STATE_ACTIVE && vb->state != VB2_BUF_STATE_ERROR) { mverr("vb buffer[%d] state is not active(%d)", vctx, video, index, vb->state); ret = -EINVAL; goto p_err; } queue->buf_com++; vb2_buffer_done(vb, state); p_err: return ret; }