1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0 2ec6859b2STodor Tomov /* 3ec6859b2STodor Tomov * camss-video.c 4ec6859b2STodor Tomov * 5ec6859b2STodor Tomov * Qualcomm MSM Camera Subsystem - V4L2 device node 6ec6859b2STodor Tomov * 7ec6859b2STodor Tomov * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. 8ec6859b2STodor Tomov * Copyright (C) 2015-2018 Linaro Ltd. 9ec6859b2STodor Tomov */ 10ec6859b2STodor Tomov #include <linux/slab.h> 11ec6859b2STodor Tomov #include <media/media-entity.h> 12ec6859b2STodor Tomov #include <media/v4l2-dev.h> 13ec6859b2STodor Tomov #include <media/v4l2-device.h> 14ec6859b2STodor Tomov #include <media/v4l2-ioctl.h> 15ec6859b2STodor Tomov #include <media/v4l2-mc.h> 16ec6859b2STodor Tomov #include <media/videobuf2-dma-sg.h> 17ec6859b2STodor Tomov 18ec6859b2STodor Tomov #include "camss-video.h" 19ec6859b2STodor Tomov #include "camss.h" 20ec6859b2STodor Tomov 2135493d65SAndrey Konovalov #define CAMSS_FRAME_MIN_WIDTH 1 2235493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_WIDTH 8191 2335493d65SAndrey Konovalov #define CAMSS_FRAME_MIN_HEIGHT 1 2435493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 2535493d65SAndrey Konovalov #define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 2635493d65SAndrey Konovalov 27ec6859b2STodor Tomov struct fract { 28ec6859b2STodor Tomov u8 numerator; 29ec6859b2STodor Tomov u8 denominator; 30ec6859b2STodor Tomov }; 31ec6859b2STodor Tomov 32ec6859b2STodor Tomov /* 33ec6859b2STodor Tomov * struct camss_format_info - ISP media bus format information 34ec6859b2STodor Tomov * @code: V4L2 media bus format code 35ec6859b2STodor Tomov * @pixelformat: V4L2 pixel format FCC identifier 36ec6859b2STodor Tomov * @planes: Number of planes 37ec6859b2STodor Tomov * @hsub: Horizontal subsampling (for each plane) 38ec6859b2STodor Tomov * @vsub: Vertical subsampling (for each plane) 39ec6859b2STodor Tomov * @bpp: Bits per pixel when stored in memory (for each plane) 40ec6859b2STodor Tomov */ 41ec6859b2STodor Tomov struct camss_format_info { 42ec6859b2STodor Tomov u32 code; 43ec6859b2STodor Tomov u32 pixelformat; 44ec6859b2STodor Tomov u8 planes; 45ec6859b2STodor Tomov struct fract hsub[3]; 46ec6859b2STodor Tomov struct fract vsub[3]; 47ec6859b2STodor Tomov unsigned int bpp[3]; 48ec6859b2STodor Tomov }; 49ec6859b2STodor Tomov 50cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x16[] = { 51ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, 52ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 53ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, 54ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 55ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, 56ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 57ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, 58ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 59ec6859b2STodor Tomov { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, 60ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 61ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, 62ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 63ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, 64ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 65ec6859b2STodor Tomov { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, 66ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 67ec6859b2STodor Tomov { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, 68ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 69ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, 70ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 71ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, 72ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 73ec6859b2STodor Tomov { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, 74ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 75ec6859b2STodor Tomov { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, 76ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 77ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, 78ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 79ec6859b2STodor Tomov { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, 80ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 81ec6859b2STodor Tomov { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, 82ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 83cc8fe073STodor Tomov { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, 84cc8fe073STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 85ec6859b2STodor Tomov }; 86ec6859b2STodor Tomov 87cba3819dSTodor Tomov static const struct camss_format_info formats_rdi_8x96[] = { 88cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, 89cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 90cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, 91cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 92cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, 93cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 94cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, 95cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 96cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, 97cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 98cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, 99cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 100cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, 101cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 102cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, 103cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 104cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, 105cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 106cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, 107cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 108cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, 109cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 110cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, 111cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1125019d7c8STodor Tomov { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, 1135019d7c8STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 114cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, 115cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 116cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, 117cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 118cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, 119cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 120cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, 121cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 122f476fb56STodor Tomov { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, 123f476fb56STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 124f476fb56STodor Tomov { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, 125f476fb56STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 126f476fb56STodor Tomov { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, 127f476fb56STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 128f476fb56STodor Tomov { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, 129f476fb56STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 130cc8fe073STodor Tomov { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, 131cc8fe073STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 132cc8fe073STodor Tomov { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, 133cc8fe073STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 134cba3819dSTodor Tomov }; 135cba3819dSTodor Tomov 136cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x16[] = { 137cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, 138cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 139cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, 140cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 141cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 142cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 143cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 144cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 145cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, 146cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 147cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, 148cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 149cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 150cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 151cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 152cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 153cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, 154cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 155cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, 156cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 157cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, 158cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 159cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, 160cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 161cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, 162cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 163cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, 164cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 165cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, 166cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 167cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, 168cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 169cba3819dSTodor Tomov }; 170cba3819dSTodor Tomov 171cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x96[] = { 172ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, 173ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 174ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, 175ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 176ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 177ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 178ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 179ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 180ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, 181ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 182ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, 183ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 184ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 185ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 186ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 187ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 188ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, 189ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 190ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, 191ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 192ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, 193ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 194ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, 195ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 196ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, 197ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 198ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, 199ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 200ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, 201ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 202ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, 203ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 204312e1c85STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, 205312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 206312e1c85STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, 207312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 208312e1c85STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, 209312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 210312e1c85STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, 211312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 212ec6859b2STodor Tomov }; 213ec6859b2STodor Tomov 214ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 215ec6859b2STodor Tomov * Helper functions 216ec6859b2STodor Tomov */ 217ec6859b2STodor Tomov 218ec6859b2STodor Tomov static int video_find_format(u32 code, u32 pixelformat, 219ec6859b2STodor Tomov const struct camss_format_info *formats, 220ec6859b2STodor Tomov unsigned int nformats) 221ec6859b2STodor Tomov { 222ec6859b2STodor Tomov int i; 223ec6859b2STodor Tomov 224ec6859b2STodor Tomov for (i = 0; i < nformats; i++) { 225ec6859b2STodor Tomov if (formats[i].code == code && 226ec6859b2STodor Tomov formats[i].pixelformat == pixelformat) 227ec6859b2STodor Tomov return i; 228ec6859b2STodor Tomov } 229ec6859b2STodor Tomov 230ec6859b2STodor Tomov for (i = 0; i < nformats; i++) 231ec6859b2STodor Tomov if (formats[i].code == code) 232ec6859b2STodor Tomov return i; 233ec6859b2STodor Tomov 234ec6859b2STodor Tomov WARN_ON(1); 235ec6859b2STodor Tomov 236ec6859b2STodor Tomov return -EINVAL; 237ec6859b2STodor Tomov } 238ec6859b2STodor Tomov 239ec6859b2STodor Tomov /* 240ec6859b2STodor Tomov * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane 241ec6859b2STodor Tomov * @mbus: v4l2_mbus_framefmt format (input) 242ec6859b2STodor Tomov * @pix: v4l2_pix_format_mplane format (output) 243ec6859b2STodor Tomov * @f: a pointer to formats array element to be used for the conversion 244ec6859b2STodor Tomov * @alignment: bytesperline alignment value 245ec6859b2STodor Tomov * 246ec6859b2STodor Tomov * Fill the output pix structure with information from the input mbus format. 247ec6859b2STodor Tomov * 248ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 249ec6859b2STodor Tomov */ 250ec6859b2STodor Tomov static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, 251ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix, 252ec6859b2STodor Tomov const struct camss_format_info *f, 253ec6859b2STodor Tomov unsigned int alignment) 254ec6859b2STodor Tomov { 255ec6859b2STodor Tomov unsigned int i; 256ec6859b2STodor Tomov u32 bytesperline; 257ec6859b2STodor Tomov 258ec6859b2STodor Tomov memset(pix, 0, sizeof(*pix)); 259ec6859b2STodor Tomov v4l2_fill_pix_format_mplane(pix, mbus); 260ec6859b2STodor Tomov pix->pixelformat = f->pixelformat; 261ec6859b2STodor Tomov pix->num_planes = f->planes; 262ec6859b2STodor Tomov for (i = 0; i < pix->num_planes; i++) { 263ec6859b2STodor Tomov bytesperline = pix->width / f->hsub[i].numerator * 264ec6859b2STodor Tomov f->hsub[i].denominator * f->bpp[i] / 8; 265ec6859b2STodor Tomov bytesperline = ALIGN(bytesperline, alignment); 266ec6859b2STodor Tomov pix->plane_fmt[i].bytesperline = bytesperline; 267ec6859b2STodor Tomov pix->plane_fmt[i].sizeimage = pix->height / 268ec6859b2STodor Tomov f->vsub[i].numerator * f->vsub[i].denominator * 269ec6859b2STodor Tomov bytesperline; 270ec6859b2STodor Tomov } 271ec6859b2STodor Tomov 272ec6859b2STodor Tomov return 0; 273ec6859b2STodor Tomov } 274ec6859b2STodor Tomov 275ec6859b2STodor Tomov static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, 276ec6859b2STodor Tomov u32 *pad) 277ec6859b2STodor Tomov { 278ec6859b2STodor Tomov struct media_pad *remote; 279ec6859b2STodor Tomov 280ec6859b2STodor Tomov remote = media_entity_remote_pad(&video->pad); 281ec6859b2STodor Tomov 282ec6859b2STodor Tomov if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 283ec6859b2STodor Tomov return NULL; 284ec6859b2STodor Tomov 285ec6859b2STodor Tomov if (pad) 286ec6859b2STodor Tomov *pad = remote->index; 287ec6859b2STodor Tomov 288ec6859b2STodor Tomov return media_entity_to_v4l2_subdev(remote->entity); 289ec6859b2STodor Tomov } 290ec6859b2STodor Tomov 291ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video, 292ec6859b2STodor Tomov struct v4l2_format *format) 293ec6859b2STodor Tomov { 294ec6859b2STodor Tomov struct v4l2_subdev_format fmt; 295ec6859b2STodor Tomov struct v4l2_subdev *subdev; 296ec6859b2STodor Tomov u32 pad; 297ec6859b2STodor Tomov int ret; 298ec6859b2STodor Tomov 299ec6859b2STodor Tomov subdev = video_remote_subdev(video, &pad); 300ec6859b2STodor Tomov if (subdev == NULL) 301ec6859b2STodor Tomov return -EPIPE; 302ec6859b2STodor Tomov 303ec6859b2STodor Tomov fmt.pad = pad; 304ec6859b2STodor Tomov fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 305ec6859b2STodor Tomov 306ec6859b2STodor Tomov ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 307ec6859b2STodor Tomov if (ret) 308ec6859b2STodor Tomov return ret; 309ec6859b2STodor Tomov 310ec6859b2STodor Tomov ret = video_find_format(fmt.format.code, 311ec6859b2STodor Tomov format->fmt.pix_mp.pixelformat, 312ec6859b2STodor Tomov video->formats, video->nformats); 313ec6859b2STodor Tomov if (ret < 0) 314ec6859b2STodor Tomov return ret; 315ec6859b2STodor Tomov 316ec6859b2STodor Tomov format->type = video->type; 317ec6859b2STodor Tomov 318ec6859b2STodor Tomov return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, 319ec6859b2STodor Tomov &video->formats[ret], video->bpl_alignment); 320ec6859b2STodor Tomov } 321ec6859b2STodor Tomov 322ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 323ec6859b2STodor Tomov * Video queue operations 324ec6859b2STodor Tomov */ 325ec6859b2STodor Tomov 326ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q, 327ec6859b2STodor Tomov unsigned int *num_buffers, unsigned int *num_planes, 328ec6859b2STodor Tomov unsigned int sizes[], struct device *alloc_devs[]) 329ec6859b2STodor Tomov { 330ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 331ec6859b2STodor Tomov const struct v4l2_pix_format_mplane *format = 332ec6859b2STodor Tomov &video->active_fmt.fmt.pix_mp; 333ec6859b2STodor Tomov unsigned int i; 334ec6859b2STodor Tomov 335ec6859b2STodor Tomov if (*num_planes) { 336ec6859b2STodor Tomov if (*num_planes != format->num_planes) 337ec6859b2STodor Tomov return -EINVAL; 338ec6859b2STodor Tomov 339ec6859b2STodor Tomov for (i = 0; i < *num_planes; i++) 340ec6859b2STodor Tomov if (sizes[i] < format->plane_fmt[i].sizeimage) 341ec6859b2STodor Tomov return -EINVAL; 342ec6859b2STodor Tomov 343ec6859b2STodor Tomov return 0; 344ec6859b2STodor Tomov } 345ec6859b2STodor Tomov 346ec6859b2STodor Tomov *num_planes = format->num_planes; 347ec6859b2STodor Tomov 348ec6859b2STodor Tomov for (i = 0; i < *num_planes; i++) 349ec6859b2STodor Tomov sizes[i] = format->plane_fmt[i].sizeimage; 350ec6859b2STodor Tomov 351ec6859b2STodor Tomov return 0; 352ec6859b2STodor Tomov } 353ec6859b2STodor Tomov 354ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb) 355ec6859b2STodor Tomov { 356ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 357ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 358ec6859b2STodor Tomov struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, 359ec6859b2STodor Tomov vb); 360ec6859b2STodor Tomov const struct v4l2_pix_format_mplane *format = 361ec6859b2STodor Tomov &video->active_fmt.fmt.pix_mp; 362ec6859b2STodor Tomov struct sg_table *sgt; 363ec6859b2STodor Tomov unsigned int i; 364ec6859b2STodor Tomov 365ec6859b2STodor Tomov for (i = 0; i < format->num_planes; i++) { 366ec6859b2STodor Tomov sgt = vb2_dma_sg_plane_desc(vb, i); 367ec6859b2STodor Tomov if (!sgt) 368ec6859b2STodor Tomov return -EFAULT; 369ec6859b2STodor Tomov 370ec6859b2STodor Tomov buffer->addr[i] = sg_dma_address(sgt->sgl); 371ec6859b2STodor Tomov } 372ec6859b2STodor Tomov 373ec6859b2STodor Tomov if (format->pixelformat == V4L2_PIX_FMT_NV12 || 374ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV21 || 375ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV16 || 376ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV61) 377ec6859b2STodor Tomov buffer->addr[1] = buffer->addr[0] + 378ec6859b2STodor Tomov format->plane_fmt[0].bytesperline * 379ec6859b2STodor Tomov format->height; 380ec6859b2STodor Tomov 381ec6859b2STodor Tomov return 0; 382ec6859b2STodor Tomov } 383ec6859b2STodor Tomov 384ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb) 385ec6859b2STodor Tomov { 386ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 387ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 388ec6859b2STodor Tomov const struct v4l2_pix_format_mplane *format = 389ec6859b2STodor Tomov &video->active_fmt.fmt.pix_mp; 390ec6859b2STodor Tomov unsigned int i; 391ec6859b2STodor Tomov 392ec6859b2STodor Tomov for (i = 0; i < format->num_planes; i++) { 393ec6859b2STodor Tomov if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i)) 394ec6859b2STodor Tomov return -EINVAL; 395ec6859b2STodor Tomov 396ec6859b2STodor Tomov vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage); 397ec6859b2STodor Tomov } 398ec6859b2STodor Tomov 399ec6859b2STodor Tomov vbuf->field = V4L2_FIELD_NONE; 400ec6859b2STodor Tomov 401ec6859b2STodor Tomov return 0; 402ec6859b2STodor Tomov } 403ec6859b2STodor Tomov 404ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb) 405ec6859b2STodor Tomov { 406ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 407ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 408ec6859b2STodor Tomov struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, 409ec6859b2STodor Tomov vb); 410ec6859b2STodor Tomov 411ec6859b2STodor Tomov video->ops->queue_buffer(video, buffer); 412ec6859b2STodor Tomov } 413ec6859b2STodor Tomov 414ec6859b2STodor Tomov static int video_check_format(struct camss_video *video) 415ec6859b2STodor Tomov { 416ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp; 417ec6859b2STodor Tomov struct v4l2_format format; 418ec6859b2STodor Tomov struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp; 419ec6859b2STodor Tomov int ret; 420ec6859b2STodor Tomov 421ec6859b2STodor Tomov sd_pix->pixelformat = pix->pixelformat; 422ec6859b2STodor Tomov ret = video_get_subdev_format(video, &format); 423ec6859b2STodor Tomov if (ret < 0) 424ec6859b2STodor Tomov return ret; 425ec6859b2STodor Tomov 426ec6859b2STodor Tomov if (pix->pixelformat != sd_pix->pixelformat || 427ec6859b2STodor Tomov pix->height != sd_pix->height || 428ec6859b2STodor Tomov pix->width != sd_pix->width || 429ec6859b2STodor Tomov pix->num_planes != sd_pix->num_planes || 430ec6859b2STodor Tomov pix->field != format.fmt.pix_mp.field) 431ec6859b2STodor Tomov return -EPIPE; 432ec6859b2STodor Tomov 433ec6859b2STodor Tomov return 0; 434ec6859b2STodor Tomov } 435ec6859b2STodor Tomov 436ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count) 437ec6859b2STodor Tomov { 438ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 439ec6859b2STodor Tomov struct video_device *vdev = &video->vdev; 440ec6859b2STodor Tomov struct media_entity *entity; 441ec6859b2STodor Tomov struct media_pad *pad; 442ec6859b2STodor Tomov struct v4l2_subdev *subdev; 443ec6859b2STodor Tomov int ret; 444ec6859b2STodor Tomov 445ec6859b2STodor Tomov ret = media_pipeline_start(&vdev->entity, &video->pipe); 446ec6859b2STodor Tomov if (ret < 0) 447ec6859b2STodor Tomov return ret; 448ec6859b2STodor Tomov 449ec6859b2STodor Tomov ret = video_check_format(video); 450ec6859b2STodor Tomov if (ret < 0) 451ec6859b2STodor Tomov goto error; 452ec6859b2STodor Tomov 453ec6859b2STodor Tomov entity = &vdev->entity; 454ec6859b2STodor Tomov while (1) { 455ec6859b2STodor Tomov pad = &entity->pads[0]; 456ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 457ec6859b2STodor Tomov break; 458ec6859b2STodor Tomov 459ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 460ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 461ec6859b2STodor Tomov break; 462ec6859b2STodor Tomov 463ec6859b2STodor Tomov entity = pad->entity; 464ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(entity); 465ec6859b2STodor Tomov 466ec6859b2STodor Tomov ret = v4l2_subdev_call(subdev, video, s_stream, 1); 467ec6859b2STodor Tomov if (ret < 0 && ret != -ENOIOCTLCMD) 468ec6859b2STodor Tomov goto error; 469ec6859b2STodor Tomov } 470ec6859b2STodor Tomov 471ec6859b2STodor Tomov return 0; 472ec6859b2STodor Tomov 473ec6859b2STodor Tomov error: 474ec6859b2STodor Tomov media_pipeline_stop(&vdev->entity); 475ec6859b2STodor Tomov 476ec6859b2STodor Tomov video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); 477ec6859b2STodor Tomov 478ec6859b2STodor Tomov return ret; 479ec6859b2STodor Tomov } 480ec6859b2STodor Tomov 481ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q) 482ec6859b2STodor Tomov { 483ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 484ec6859b2STodor Tomov struct video_device *vdev = &video->vdev; 485ec6859b2STodor Tomov struct media_entity *entity; 486ec6859b2STodor Tomov struct media_pad *pad; 487ec6859b2STodor Tomov struct v4l2_subdev *subdev; 488ec6859b2STodor Tomov 489ec6859b2STodor Tomov entity = &vdev->entity; 490ec6859b2STodor Tomov while (1) { 491ec6859b2STodor Tomov pad = &entity->pads[0]; 492ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 493ec6859b2STodor Tomov break; 494ec6859b2STodor Tomov 495ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 496ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 497ec6859b2STodor Tomov break; 498ec6859b2STodor Tomov 499ec6859b2STodor Tomov entity = pad->entity; 500ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(entity); 501ec6859b2STodor Tomov 502ec6859b2STodor Tomov v4l2_subdev_call(subdev, video, s_stream, 0); 503ec6859b2STodor Tomov } 504ec6859b2STodor Tomov 505ec6859b2STodor Tomov media_pipeline_stop(&vdev->entity); 506ec6859b2STodor Tomov 507ec6859b2STodor Tomov video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); 508ec6859b2STodor Tomov } 509ec6859b2STodor Tomov 510ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = { 511ec6859b2STodor Tomov .queue_setup = video_queue_setup, 512ec6859b2STodor Tomov .wait_prepare = vb2_ops_wait_prepare, 513ec6859b2STodor Tomov .wait_finish = vb2_ops_wait_finish, 514ec6859b2STodor Tomov .buf_init = video_buf_init, 515ec6859b2STodor Tomov .buf_prepare = video_buf_prepare, 516ec6859b2STodor Tomov .buf_queue = video_buf_queue, 517ec6859b2STodor Tomov .start_streaming = video_start_streaming, 518ec6859b2STodor Tomov .stop_streaming = video_stop_streaming, 519ec6859b2STodor Tomov }; 520ec6859b2STodor Tomov 521ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 522ec6859b2STodor Tomov * V4L2 ioctls 523ec6859b2STodor Tomov */ 524ec6859b2STodor Tomov 525ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh, 526ec6859b2STodor Tomov struct v4l2_capability *cap) 527ec6859b2STodor Tomov { 528ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 529ec6859b2STodor Tomov 530c0decac1SMauro Carvalho Chehab strscpy(cap->driver, "qcom-camss", sizeof(cap->driver)); 531c0decac1SMauro Carvalho Chehab strscpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card)); 532ec6859b2STodor Tomov snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 533ec6859b2STodor Tomov dev_name(video->camss->dev)); 534ec6859b2STodor Tomov 535ec6859b2STodor Tomov return 0; 536ec6859b2STodor Tomov } 537ec6859b2STodor Tomov 538a3d412d4SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 539ec6859b2STodor Tomov { 540a3d412d4SAndrey Konovalov struct camss_video *video = video_drvdata(file); 541ec6859b2STodor Tomov int i, j, k; 542dfb5d328SAndrey Konovalov u32 mcode = f->mbus_code; 543ec6859b2STodor Tomov 544a3d412d4SAndrey Konovalov if (f->type != video->type) 545a3d412d4SAndrey Konovalov return -EINVAL; 546a3d412d4SAndrey Konovalov 547a3d412d4SAndrey Konovalov if (f->index >= video->nformats) 548a3d412d4SAndrey Konovalov return -EINVAL; 549a3d412d4SAndrey Konovalov 550dfb5d328SAndrey Konovalov /* 551dfb5d328SAndrey Konovalov * Find index "i" of "k"th unique pixelformat in formats array. 552dfb5d328SAndrey Konovalov * 553dfb5d328SAndrey Konovalov * If f->mbus_code passed to video_enum_fmt() is not zero, a device 554dfb5d328SAndrey Konovalov * with V4L2_CAP_IO_MC capability restricts enumeration to only the 555dfb5d328SAndrey Konovalov * pixel formats that can be produced from that media bus code. 556dfb5d328SAndrey Konovalov * This is implemented by skipping video->formats[] entries with 557dfb5d328SAndrey Konovalov * code != f->mbus_code (if f->mbus_code is not zero). 558dfb5d328SAndrey Konovalov * If the f->mbus_code passed to video_enum_fmt() is not supported, 559dfb5d328SAndrey Konovalov * -EINVAL is returned. 560dfb5d328SAndrey Konovalov * If f->mbus_code is zero, all the pixel formats are enumerated. 561dfb5d328SAndrey Konovalov */ 562ec6859b2STodor Tomov k = -1; 563ec6859b2STodor Tomov for (i = 0; i < video->nformats; i++) { 564dfb5d328SAndrey Konovalov if (mcode != 0 && video->formats[i].code != mcode) 565dfb5d328SAndrey Konovalov continue; 566dfb5d328SAndrey Konovalov 567ec6859b2STodor Tomov for (j = 0; j < i; j++) { 568dfb5d328SAndrey Konovalov if (mcode != 0 && video->formats[j].code != mcode) 569dfb5d328SAndrey Konovalov continue; 570ec6859b2STodor Tomov if (video->formats[i].pixelformat == 571ec6859b2STodor Tomov video->formats[j].pixelformat) 572ec6859b2STodor Tomov break; 573ec6859b2STodor Tomov } 574ec6859b2STodor Tomov 575ec6859b2STodor Tomov if (j == i) 576ec6859b2STodor Tomov k++; 577ec6859b2STodor Tomov 578a3d412d4SAndrey Konovalov if (k == f->index) 579a3d412d4SAndrey Konovalov break; 580ec6859b2STodor Tomov } 581ec6859b2STodor Tomov 582b00481bdSDan Carpenter if (k == -1 || k < f->index) 583dfb5d328SAndrey Konovalov /* 584dfb5d328SAndrey Konovalov * All the unique pixel formats matching the arguments 585dfb5d328SAndrey Konovalov * have been enumerated (k >= 0 and f->index > 0), or 586dfb5d328SAndrey Konovalov * no pixel formats match the non-zero f->mbus_code (k == -1). 587dfb5d328SAndrey Konovalov */ 588ec6859b2STodor Tomov return -EINVAL; 589ec6859b2STodor Tomov 590ec6859b2STodor Tomov f->pixelformat = video->formats[i].pixelformat; 591ec6859b2STodor Tomov 592ec6859b2STodor Tomov return 0; 593ec6859b2STodor Tomov } 594ec6859b2STodor Tomov 59535493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh, 59635493d65SAndrey Konovalov struct v4l2_frmsizeenum *fsize) 59735493d65SAndrey Konovalov { 59835493d65SAndrey Konovalov struct camss_video *video = video_drvdata(file); 59935493d65SAndrey Konovalov int i; 60035493d65SAndrey Konovalov 60135493d65SAndrey Konovalov if (fsize->index) 60235493d65SAndrey Konovalov return -EINVAL; 60335493d65SAndrey Konovalov 60435493d65SAndrey Konovalov /* Only accept pixel format present in the formats[] table */ 60535493d65SAndrey Konovalov for (i = 0; i < video->nformats; i++) { 60635493d65SAndrey Konovalov if (video->formats[i].pixelformat == fsize->pixel_format) 60735493d65SAndrey Konovalov break; 60835493d65SAndrey Konovalov } 60935493d65SAndrey Konovalov 61035493d65SAndrey Konovalov if (i == video->nformats) 61135493d65SAndrey Konovalov return -EINVAL; 61235493d65SAndrey Konovalov 61335493d65SAndrey Konovalov fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 61435493d65SAndrey Konovalov fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH; 61535493d65SAndrey Konovalov fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH; 61635493d65SAndrey Konovalov fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT; 61735493d65SAndrey Konovalov fsize->stepwise.max_height = (video->line_based) ? 61835493d65SAndrey Konovalov CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI; 61935493d65SAndrey Konovalov fsize->stepwise.step_width = 1; 62035493d65SAndrey Konovalov fsize->stepwise.step_height = 1; 62135493d65SAndrey Konovalov 62235493d65SAndrey Konovalov return 0; 62335493d65SAndrey Konovalov } 62435493d65SAndrey Konovalov 625ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 626ec6859b2STodor Tomov { 627ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 628ec6859b2STodor Tomov 629ec6859b2STodor Tomov *f = video->active_fmt; 630ec6859b2STodor Tomov 631ec6859b2STodor Tomov return 0; 632ec6859b2STodor Tomov } 633ec6859b2STodor Tomov 634ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 635ec6859b2STodor Tomov { 636ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix_mp; 637ec6859b2STodor Tomov const struct camss_format_info *fi; 638ec6859b2STodor Tomov struct v4l2_plane_pix_format *p; 639ec6859b2STodor Tomov u32 bytesperline[3] = { 0 }; 640ec6859b2STodor Tomov u32 sizeimage[3] = { 0 }; 641ec6859b2STodor Tomov u32 width, height; 642ec6859b2STodor Tomov u32 bpl, lines; 643ec6859b2STodor Tomov int i, j; 644ec6859b2STodor Tomov 645ec6859b2STodor Tomov pix_mp = &f->fmt.pix_mp; 646ec6859b2STodor Tomov 647ec6859b2STodor Tomov if (video->line_based) 648ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes && i < 3; i++) { 649ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 650ec6859b2STodor Tomov bytesperline[i] = clamp_t(u32, p->bytesperline, 651ec6859b2STodor Tomov 1, 65528); 652ec6859b2STodor Tomov sizeimage[i] = clamp_t(u32, p->sizeimage, 653ec6859b2STodor Tomov bytesperline[i], 654daf2298bSAndrey Konovalov bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX); 655ec6859b2STodor Tomov } 656ec6859b2STodor Tomov 657ec6859b2STodor Tomov for (j = 0; j < video->nformats; j++) 658ec6859b2STodor Tomov if (pix_mp->pixelformat == video->formats[j].pixelformat) 659ec6859b2STodor Tomov break; 660ec6859b2STodor Tomov 661ec6859b2STodor Tomov if (j == video->nformats) 662ec6859b2STodor Tomov j = 0; /* default format */ 663ec6859b2STodor Tomov 664ec6859b2STodor Tomov fi = &video->formats[j]; 665ec6859b2STodor Tomov width = pix_mp->width; 666ec6859b2STodor Tomov height = pix_mp->height; 667ec6859b2STodor Tomov 668ec6859b2STodor Tomov memset(pix_mp, 0, sizeof(*pix_mp)); 669ec6859b2STodor Tomov 670ec6859b2STodor Tomov pix_mp->pixelformat = fi->pixelformat; 671daf2298bSAndrey Konovalov pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH); 672daf2298bSAndrey Konovalov pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI); 673ec6859b2STodor Tomov pix_mp->num_planes = fi->planes; 674ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 675ec6859b2STodor Tomov bpl = pix_mp->width / fi->hsub[i].numerator * 676ec6859b2STodor Tomov fi->hsub[i].denominator * fi->bpp[i] / 8; 677ec6859b2STodor Tomov bpl = ALIGN(bpl, video->bpl_alignment); 678ec6859b2STodor Tomov pix_mp->plane_fmt[i].bytesperline = bpl; 679ec6859b2STodor Tomov pix_mp->plane_fmt[i].sizeimage = pix_mp->height / 680ec6859b2STodor Tomov fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; 681ec6859b2STodor Tomov } 682ec6859b2STodor Tomov 683ec6859b2STodor Tomov pix_mp->field = V4L2_FIELD_NONE; 684ec6859b2STodor Tomov pix_mp->colorspace = V4L2_COLORSPACE_SRGB; 685ec6859b2STodor Tomov pix_mp->flags = 0; 686ec6859b2STodor Tomov pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace); 687ec6859b2STodor Tomov pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 688ec6859b2STodor Tomov pix_mp->colorspace, pix_mp->ycbcr_enc); 689ec6859b2STodor Tomov pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace); 690ec6859b2STodor Tomov 691ec6859b2STodor Tomov if (video->line_based) 692ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 693ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 694ec6859b2STodor Tomov p->bytesperline = clamp_t(u32, p->bytesperline, 695ec6859b2STodor Tomov 1, 65528); 696ec6859b2STodor Tomov p->sizeimage = clamp_t(u32, p->sizeimage, 697ec6859b2STodor Tomov p->bytesperline, 698daf2298bSAndrey Konovalov p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX); 699ec6859b2STodor Tomov lines = p->sizeimage / p->bytesperline; 700ec6859b2STodor Tomov 701ec6859b2STodor Tomov if (p->bytesperline < bytesperline[i]) 702ec6859b2STodor Tomov p->bytesperline = ALIGN(bytesperline[i], 8); 703ec6859b2STodor Tomov 704ec6859b2STodor Tomov if (p->sizeimage < p->bytesperline * lines) 705ec6859b2STodor Tomov p->sizeimage = p->bytesperline * lines; 706ec6859b2STodor Tomov 707ec6859b2STodor Tomov if (p->sizeimage < sizeimage[i]) 708ec6859b2STodor Tomov p->sizeimage = sizeimage[i]; 709ec6859b2STodor Tomov } 710ec6859b2STodor Tomov 711ec6859b2STodor Tomov return 0; 712ec6859b2STodor Tomov } 713ec6859b2STodor Tomov 714ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 715ec6859b2STodor Tomov { 716ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 717ec6859b2STodor Tomov 718ec6859b2STodor Tomov return __video_try_fmt(video, f); 719ec6859b2STodor Tomov } 720ec6859b2STodor Tomov 721ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 722ec6859b2STodor Tomov { 723ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 724ec6859b2STodor Tomov int ret; 725ec6859b2STodor Tomov 726ec6859b2STodor Tomov if (vb2_is_busy(&video->vb2_q)) 727ec6859b2STodor Tomov return -EBUSY; 728ec6859b2STodor Tomov 729ec6859b2STodor Tomov ret = __video_try_fmt(video, f); 730ec6859b2STodor Tomov if (ret < 0) 731ec6859b2STodor Tomov return ret; 732ec6859b2STodor Tomov 733ec6859b2STodor Tomov video->active_fmt = *f; 734ec6859b2STodor Tomov 735ec6859b2STodor Tomov return 0; 736ec6859b2STodor Tomov } 737ec6859b2STodor Tomov 738ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh, 739ec6859b2STodor Tomov struct v4l2_input *input) 740ec6859b2STodor Tomov { 741ec6859b2STodor Tomov if (input->index > 0) 742ec6859b2STodor Tomov return -EINVAL; 743ec6859b2STodor Tomov 744c0decac1SMauro Carvalho Chehab strscpy(input->name, "camera", sizeof(input->name)); 745ec6859b2STodor Tomov input->type = V4L2_INPUT_TYPE_CAMERA; 746ec6859b2STodor Tomov 747ec6859b2STodor Tomov return 0; 748ec6859b2STodor Tomov } 749ec6859b2STodor Tomov 750ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input) 751ec6859b2STodor Tomov { 752ec6859b2STodor Tomov *input = 0; 753ec6859b2STodor Tomov 754ec6859b2STodor Tomov return 0; 755ec6859b2STodor Tomov } 756ec6859b2STodor Tomov 757ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input) 758ec6859b2STodor Tomov { 759ec6859b2STodor Tomov return input == 0 ? 0 : -EINVAL; 760ec6859b2STodor Tomov } 761ec6859b2STodor Tomov 762ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { 763ec6859b2STodor Tomov .vidioc_querycap = video_querycap, 7647e98b7b5SBoris Brezillon .vidioc_enum_fmt_vid_cap = video_enum_fmt, 76535493d65SAndrey Konovalov .vidioc_enum_framesizes = video_enum_framesizes, 766ec6859b2STodor Tomov .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, 767ec6859b2STodor Tomov .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, 768ec6859b2STodor Tomov .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, 769ec6859b2STodor Tomov .vidioc_reqbufs = vb2_ioctl_reqbufs, 770ec6859b2STodor Tomov .vidioc_querybuf = vb2_ioctl_querybuf, 771ec6859b2STodor Tomov .vidioc_qbuf = vb2_ioctl_qbuf, 772ec6859b2STodor Tomov .vidioc_expbuf = vb2_ioctl_expbuf, 773ec6859b2STodor Tomov .vidioc_dqbuf = vb2_ioctl_dqbuf, 774ec6859b2STodor Tomov .vidioc_create_bufs = vb2_ioctl_create_bufs, 775ec6859b2STodor Tomov .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 776ec6859b2STodor Tomov .vidioc_streamon = vb2_ioctl_streamon, 777ec6859b2STodor Tomov .vidioc_streamoff = vb2_ioctl_streamoff, 778ec6859b2STodor Tomov .vidioc_enum_input = video_enum_input, 779ec6859b2STodor Tomov .vidioc_g_input = video_g_input, 780ec6859b2STodor Tomov .vidioc_s_input = video_s_input, 781ec6859b2STodor Tomov }; 782ec6859b2STodor Tomov 783ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 784ec6859b2STodor Tomov * V4L2 file operations 785ec6859b2STodor Tomov */ 786ec6859b2STodor Tomov 787ec6859b2STodor Tomov static int video_open(struct file *file) 788ec6859b2STodor Tomov { 789ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 790ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 791ec6859b2STodor Tomov struct v4l2_fh *vfh; 792ec6859b2STodor Tomov int ret; 793ec6859b2STodor Tomov 794ec6859b2STodor Tomov mutex_lock(&video->lock); 795ec6859b2STodor Tomov 796ec6859b2STodor Tomov vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); 797ec6859b2STodor Tomov if (vfh == NULL) { 798ec6859b2STodor Tomov ret = -ENOMEM; 799ec6859b2STodor Tomov goto error_alloc; 800ec6859b2STodor Tomov } 801ec6859b2STodor Tomov 802ec6859b2STodor Tomov v4l2_fh_init(vfh, vdev); 803ec6859b2STodor Tomov v4l2_fh_add(vfh); 804ec6859b2STodor Tomov 805ec6859b2STodor Tomov file->private_data = vfh; 806ec6859b2STodor Tomov 8078fd390b8SEzequiel Garcia ret = v4l2_pipeline_pm_get(&vdev->entity); 808ec6859b2STodor Tomov if (ret < 0) { 809ec6859b2STodor Tomov dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", 810ec6859b2STodor Tomov ret); 811ec6859b2STodor Tomov goto error_pm_use; 812ec6859b2STodor Tomov } 813ec6859b2STodor Tomov 814ec6859b2STodor Tomov mutex_unlock(&video->lock); 815ec6859b2STodor Tomov 816ec6859b2STodor Tomov return 0; 817ec6859b2STodor Tomov 818ec6859b2STodor Tomov error_pm_use: 819ec6859b2STodor Tomov v4l2_fh_release(file); 820ec6859b2STodor Tomov 821ec6859b2STodor Tomov error_alloc: 822ec6859b2STodor Tomov mutex_unlock(&video->lock); 823ec6859b2STodor Tomov 824ec6859b2STodor Tomov return ret; 825ec6859b2STodor Tomov } 826ec6859b2STodor Tomov 827ec6859b2STodor Tomov static int video_release(struct file *file) 828ec6859b2STodor Tomov { 829ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 830ec6859b2STodor Tomov 831ec6859b2STodor Tomov vb2_fop_release(file); 832ec6859b2STodor Tomov 8338fd390b8SEzequiel Garcia v4l2_pipeline_pm_put(&vdev->entity); 834ec6859b2STodor Tomov 835ec6859b2STodor Tomov file->private_data = NULL; 836ec6859b2STodor Tomov 837ec6859b2STodor Tomov return 0; 838ec6859b2STodor Tomov } 839ec6859b2STodor Tomov 840ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = { 841ec6859b2STodor Tomov .owner = THIS_MODULE, 842ec6859b2STodor Tomov .unlocked_ioctl = video_ioctl2, 843ec6859b2STodor Tomov .open = video_open, 844ec6859b2STodor Tomov .release = video_release, 845ec6859b2STodor Tomov .poll = vb2_fop_poll, 846ec6859b2STodor Tomov .mmap = vb2_fop_mmap, 847ec6859b2STodor Tomov .read = vb2_fop_read, 848ec6859b2STodor Tomov }; 849ec6859b2STodor Tomov 850ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 851ec6859b2STodor Tomov * CAMSS video core 852ec6859b2STodor Tomov */ 853ec6859b2STodor Tomov 854ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev) 855ec6859b2STodor Tomov { 856ec6859b2STodor Tomov struct camss_video *video = video_get_drvdata(vdev); 857ec6859b2STodor Tomov 858ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 859ec6859b2STodor Tomov 860ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 861ec6859b2STodor Tomov mutex_destroy(&video->lock); 862ec6859b2STodor Tomov 863ec6859b2STodor Tomov if (atomic_dec_and_test(&video->camss->ref_count)) 864ec6859b2STodor Tomov camss_delete(video->camss); 865ec6859b2STodor Tomov } 866ec6859b2STodor Tomov 867ec6859b2STodor Tomov /* 868ec6859b2STodor Tomov * msm_video_init_format - Helper function to initialize format 869ec6859b2STodor Tomov * @video: struct camss_video 870ec6859b2STodor Tomov * 871ec6859b2STodor Tomov * Initialize pad format with default value. 872ec6859b2STodor Tomov * 873ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 874ec6859b2STodor Tomov */ 875ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video) 876ec6859b2STodor Tomov { 877ec6859b2STodor Tomov int ret; 878ec6859b2STodor Tomov struct v4l2_format format = { 879ec6859b2STodor Tomov .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 880ec6859b2STodor Tomov .fmt.pix_mp = { 881ec6859b2STodor Tomov .width = 1920, 882ec6859b2STodor Tomov .height = 1080, 883ec6859b2STodor Tomov .pixelformat = video->formats[0].pixelformat, 884ec6859b2STodor Tomov }, 885ec6859b2STodor Tomov }; 886ec6859b2STodor Tomov 887ec6859b2STodor Tomov ret = __video_try_fmt(video, &format); 888ec6859b2STodor Tomov if (ret < 0) 889ec6859b2STodor Tomov return ret; 890ec6859b2STodor Tomov 891ec6859b2STodor Tomov video->active_fmt = format; 892ec6859b2STodor Tomov 893ec6859b2STodor Tomov return 0; 894ec6859b2STodor Tomov } 895ec6859b2STodor Tomov 896ec6859b2STodor Tomov /* 897ec6859b2STodor Tomov * msm_video_register - Register a video device node 898ec6859b2STodor Tomov * @video: struct camss_video 899ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 900ec6859b2STodor Tomov * @name: name to be used for the video device node 901ec6859b2STodor Tomov * 902ec6859b2STodor Tomov * Initialize and register a video device node to a V4L2 device. Also 903ec6859b2STodor Tomov * initialize the vb2 queue. 904ec6859b2STodor Tomov * 905ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 906ec6859b2STodor Tomov */ 907ec6859b2STodor Tomov 908ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, 909ec6859b2STodor Tomov const char *name, int is_pix) 910ec6859b2STodor Tomov { 911ec6859b2STodor Tomov struct media_pad *pad = &video->pad; 912ec6859b2STodor Tomov struct video_device *vdev; 913ec6859b2STodor Tomov struct vb2_queue *q; 914ec6859b2STodor Tomov int ret; 915ec6859b2STodor Tomov 916ec6859b2STodor Tomov vdev = &video->vdev; 917ec6859b2STodor Tomov 918ec6859b2STodor Tomov mutex_init(&video->q_lock); 919ec6859b2STodor Tomov 920ec6859b2STodor Tomov q = &video->vb2_q; 921ec6859b2STodor Tomov q->drv_priv = video; 922ec6859b2STodor Tomov q->mem_ops = &vb2_dma_sg_memops; 923ec6859b2STodor Tomov q->ops = &msm_video_vb2_q_ops; 924ec6859b2STodor Tomov q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 925ec6859b2STodor Tomov q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; 926ec6859b2STodor Tomov q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 927ec6859b2STodor Tomov q->buf_struct_size = sizeof(struct camss_buffer); 928ec6859b2STodor Tomov q->dev = video->camss->dev; 929ec6859b2STodor Tomov q->lock = &video->q_lock; 930ec6859b2STodor Tomov ret = vb2_queue_init(q); 931ec6859b2STodor Tomov if (ret < 0) { 932ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); 933ec6859b2STodor Tomov goto error_vb2_init; 934ec6859b2STodor Tomov } 935ec6859b2STodor Tomov 936ec6859b2STodor Tomov pad->flags = MEDIA_PAD_FL_SINK; 937ec6859b2STodor Tomov ret = media_entity_pads_init(&vdev->entity, 1, pad); 938ec6859b2STodor Tomov if (ret < 0) { 939ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", 940ec6859b2STodor Tomov ret); 941492abcd7SHans Verkuil goto error_vb2_init; 942ec6859b2STodor Tomov } 943ec6859b2STodor Tomov 944ec6859b2STodor Tomov mutex_init(&video->lock); 945ec6859b2STodor Tomov 946cba3819dSTodor Tomov if (video->camss->version == CAMSS_8x16) { 947ec6859b2STodor Tomov if (is_pix) { 948cba3819dSTodor Tomov video->formats = formats_pix_8x16; 949cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x16); 950cba3819dSTodor Tomov } else { 951cba3819dSTodor Tomov video->formats = formats_rdi_8x16; 952cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x16); 953cba3819dSTodor Tomov } 9549e5d1581SAngeloGioacchino Del Regno } else if (video->camss->version == CAMSS_8x96 || 9559e5d1581SAngeloGioacchino Del Regno video->camss->version == CAMSS_660) { 956cba3819dSTodor Tomov if (is_pix) { 957cba3819dSTodor Tomov video->formats = formats_pix_8x96; 958cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x96); 959cba3819dSTodor Tomov } else { 960cba3819dSTodor Tomov video->formats = formats_rdi_8x96; 961cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x96); 962cba3819dSTodor Tomov } 963cba3819dSTodor Tomov } else { 964*9c67ed2aSDan Carpenter ret = -EINVAL; 965cba3819dSTodor Tomov goto error_video_register; 966ec6859b2STodor Tomov } 967ec6859b2STodor Tomov 968ec6859b2STodor Tomov ret = msm_video_init_format(video); 969ec6859b2STodor Tomov if (ret < 0) { 970ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); 971ec6859b2STodor Tomov goto error_video_register; 972ec6859b2STodor Tomov } 973ec6859b2STodor Tomov 974ec6859b2STodor Tomov vdev->fops = &msm_vid_fops; 975dfb5d328SAndrey Konovalov vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING 976dfb5d328SAndrey Konovalov | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; 977ec6859b2STodor Tomov vdev->ioctl_ops = &msm_vid_ioctl_ops; 978ec6859b2STodor Tomov vdev->release = msm_video_release; 979ec6859b2STodor Tomov vdev->v4l2_dev = v4l2_dev; 980ec6859b2STodor Tomov vdev->vfl_dir = VFL_DIR_RX; 981ec6859b2STodor Tomov vdev->queue = &video->vb2_q; 982ec6859b2STodor Tomov vdev->lock = &video->lock; 983c0decac1SMauro Carvalho Chehab strscpy(vdev->name, name, sizeof(vdev->name)); 984ec6859b2STodor Tomov 98570cad449SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 986ec6859b2STodor Tomov if (ret < 0) { 987ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", 988ec6859b2STodor Tomov ret); 989ec6859b2STodor Tomov goto error_video_register; 990ec6859b2STodor Tomov } 991ec6859b2STodor Tomov 992ec6859b2STodor Tomov video_set_drvdata(vdev, video); 993ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 994ec6859b2STodor Tomov 995ec6859b2STodor Tomov return 0; 996ec6859b2STodor Tomov 997ec6859b2STodor Tomov error_video_register: 998ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 999ec6859b2STodor Tomov mutex_destroy(&video->lock); 1000ec6859b2STodor Tomov error_vb2_init: 1001ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 1002ec6859b2STodor Tomov 1003ec6859b2STodor Tomov return ret; 1004ec6859b2STodor Tomov } 1005ec6859b2STodor Tomov 1006ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video) 1007ec6859b2STodor Tomov { 1008ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 1009492abcd7SHans Verkuil vb2_video_unregister_device(&video->vdev); 1010ec6859b2STodor Tomov atomic_dec(&video->camss->ref_count); 1011ec6859b2STodor Tomov } 1012