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 538c90f1178SAndrey Konovalov /* 539c90f1178SAndrey Konovalov * Returns the index in the video->formats[] array of the element which 540c90f1178SAndrey Konovalov * has the "ndx"th unique value of pixelformat field. 541c90f1178SAndrey Konovalov * If not found (no more unique pixelformat's) returns -EINVAL. 542c90f1178SAndrey Konovalov */ 543c90f1178SAndrey Konovalov static int video_get_unique_pixelformat_by_index(struct camss_video *video, 544c90f1178SAndrey Konovalov int ndx) 545ec6859b2STodor Tomov { 546ec6859b2STodor Tomov int i, j, k; 547ec6859b2STodor Tomov 548ec6859b2STodor Tomov /* find index "i" of "k"th unique pixelformat in formats array */ 549ec6859b2STodor Tomov k = -1; 550ec6859b2STodor Tomov for (i = 0; i < video->nformats; i++) { 551ec6859b2STodor Tomov for (j = 0; j < i; j++) { 552ec6859b2STodor Tomov if (video->formats[i].pixelformat == 553ec6859b2STodor Tomov video->formats[j].pixelformat) 554ec6859b2STodor Tomov break; 555ec6859b2STodor Tomov } 556ec6859b2STodor Tomov 557ec6859b2STodor Tomov if (j == i) 558ec6859b2STodor Tomov k++; 559ec6859b2STodor Tomov 560c90f1178SAndrey Konovalov if (k == ndx) 561c90f1178SAndrey Konovalov return i; 562ec6859b2STodor Tomov } 563ec6859b2STodor Tomov 564c90f1178SAndrey Konovalov return -EINVAL; 565c90f1178SAndrey Konovalov } 566c90f1178SAndrey Konovalov 567c90f1178SAndrey Konovalov /* 568c90f1178SAndrey Konovalov * Returns the index in the video->formats[] array of the element which 569c90f1178SAndrey Konovalov * has code equal to mcode. 570c90f1178SAndrey Konovalov * If not found returns -EINVAL. 571c90f1178SAndrey Konovalov */ 572c90f1178SAndrey Konovalov static int video_get_pixelformat_by_mbus_code(struct camss_video *video, 573c90f1178SAndrey Konovalov u32 mcode) 574c90f1178SAndrey Konovalov { 575c90f1178SAndrey Konovalov int i; 576c90f1178SAndrey Konovalov 577c90f1178SAndrey Konovalov for (i = 0; i < video->nformats; i++) { 578c90f1178SAndrey Konovalov if (video->formats[i].code == mcode) 579c90f1178SAndrey Konovalov return i; 580c90f1178SAndrey Konovalov } 581c90f1178SAndrey Konovalov 582c90f1178SAndrey Konovalov return -EINVAL; 583c90f1178SAndrey Konovalov } 584c90f1178SAndrey Konovalov 585c90f1178SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 586c90f1178SAndrey Konovalov { 587c90f1178SAndrey Konovalov struct camss_video *video = video_drvdata(file); 588c90f1178SAndrey Konovalov int i; 589c90f1178SAndrey Konovalov 590c90f1178SAndrey Konovalov if (f->type != video->type) 591c90f1178SAndrey Konovalov return -EINVAL; 592c90f1178SAndrey Konovalov 593c90f1178SAndrey Konovalov if (f->index >= video->nformats) 594c90f1178SAndrey Konovalov return -EINVAL; 595c90f1178SAndrey Konovalov 596c90f1178SAndrey Konovalov if (f->mbus_code) { 597c90f1178SAndrey Konovalov /* Each entry in formats[] table has unique mbus_code */ 598c90f1178SAndrey Konovalov if (f->index > 0) 599c90f1178SAndrey Konovalov return -EINVAL; 600c90f1178SAndrey Konovalov 601c90f1178SAndrey Konovalov i = video_get_pixelformat_by_mbus_code(video, f->mbus_code); 602c90f1178SAndrey Konovalov } else { 603c90f1178SAndrey Konovalov i = video_get_unique_pixelformat_by_index(video, f->index); 604c90f1178SAndrey Konovalov } 605c90f1178SAndrey Konovalov 606c90f1178SAndrey Konovalov if (i < 0) 607ec6859b2STodor Tomov return -EINVAL; 608ec6859b2STodor Tomov 609ec6859b2STodor Tomov f->pixelformat = video->formats[i].pixelformat; 610ec6859b2STodor Tomov 611ec6859b2STodor Tomov return 0; 612ec6859b2STodor Tomov } 613ec6859b2STodor Tomov 61435493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh, 61535493d65SAndrey Konovalov struct v4l2_frmsizeenum *fsize) 61635493d65SAndrey Konovalov { 61735493d65SAndrey Konovalov struct camss_video *video = video_drvdata(file); 61835493d65SAndrey Konovalov int i; 61935493d65SAndrey Konovalov 62035493d65SAndrey Konovalov if (fsize->index) 62135493d65SAndrey Konovalov return -EINVAL; 62235493d65SAndrey Konovalov 62335493d65SAndrey Konovalov /* Only accept pixel format present in the formats[] table */ 62435493d65SAndrey Konovalov for (i = 0; i < video->nformats; i++) { 62535493d65SAndrey Konovalov if (video->formats[i].pixelformat == fsize->pixel_format) 62635493d65SAndrey Konovalov break; 62735493d65SAndrey Konovalov } 62835493d65SAndrey Konovalov 62935493d65SAndrey Konovalov if (i == video->nformats) 63035493d65SAndrey Konovalov return -EINVAL; 63135493d65SAndrey Konovalov 63235493d65SAndrey Konovalov fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 63335493d65SAndrey Konovalov fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH; 63435493d65SAndrey Konovalov fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH; 63535493d65SAndrey Konovalov fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT; 63635493d65SAndrey Konovalov fsize->stepwise.max_height = (video->line_based) ? 63735493d65SAndrey Konovalov CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI; 63835493d65SAndrey Konovalov fsize->stepwise.step_width = 1; 63935493d65SAndrey Konovalov fsize->stepwise.step_height = 1; 64035493d65SAndrey Konovalov 64135493d65SAndrey Konovalov return 0; 64235493d65SAndrey Konovalov } 64335493d65SAndrey Konovalov 644ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 645ec6859b2STodor Tomov { 646ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 647ec6859b2STodor Tomov 648ec6859b2STodor Tomov *f = video->active_fmt; 649ec6859b2STodor Tomov 650ec6859b2STodor Tomov return 0; 651ec6859b2STodor Tomov } 652ec6859b2STodor Tomov 653ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 654ec6859b2STodor Tomov { 655ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix_mp; 656ec6859b2STodor Tomov const struct camss_format_info *fi; 657ec6859b2STodor Tomov struct v4l2_plane_pix_format *p; 658ec6859b2STodor Tomov u32 bytesperline[3] = { 0 }; 659ec6859b2STodor Tomov u32 sizeimage[3] = { 0 }; 660ec6859b2STodor Tomov u32 width, height; 661ec6859b2STodor Tomov u32 bpl, lines; 662ec6859b2STodor Tomov int i, j; 663ec6859b2STodor Tomov 664ec6859b2STodor Tomov pix_mp = &f->fmt.pix_mp; 665ec6859b2STodor Tomov 666ec6859b2STodor Tomov if (video->line_based) 667ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes && i < 3; i++) { 668ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 669ec6859b2STodor Tomov bytesperline[i] = clamp_t(u32, p->bytesperline, 670ec6859b2STodor Tomov 1, 65528); 671ec6859b2STodor Tomov sizeimage[i] = clamp_t(u32, p->sizeimage, 672ec6859b2STodor Tomov bytesperline[i], 673ec6859b2STodor Tomov bytesperline[i] * 4096); 674ec6859b2STodor Tomov } 675ec6859b2STodor Tomov 676ec6859b2STodor Tomov for (j = 0; j < video->nformats; j++) 677ec6859b2STodor Tomov if (pix_mp->pixelformat == video->formats[j].pixelformat) 678ec6859b2STodor Tomov break; 679ec6859b2STodor Tomov 680ec6859b2STodor Tomov if (j == video->nformats) 681ec6859b2STodor Tomov j = 0; /* default format */ 682ec6859b2STodor Tomov 683ec6859b2STodor Tomov fi = &video->formats[j]; 684ec6859b2STodor Tomov width = pix_mp->width; 685ec6859b2STodor Tomov height = pix_mp->height; 686ec6859b2STodor Tomov 687ec6859b2STodor Tomov memset(pix_mp, 0, sizeof(*pix_mp)); 688ec6859b2STodor Tomov 689ec6859b2STodor Tomov pix_mp->pixelformat = fi->pixelformat; 690ec6859b2STodor Tomov pix_mp->width = clamp_t(u32, width, 1, 8191); 691ec6859b2STodor Tomov pix_mp->height = clamp_t(u32, height, 1, 8191); 692ec6859b2STodor Tomov pix_mp->num_planes = fi->planes; 693ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 694ec6859b2STodor Tomov bpl = pix_mp->width / fi->hsub[i].numerator * 695ec6859b2STodor Tomov fi->hsub[i].denominator * fi->bpp[i] / 8; 696ec6859b2STodor Tomov bpl = ALIGN(bpl, video->bpl_alignment); 697ec6859b2STodor Tomov pix_mp->plane_fmt[i].bytesperline = bpl; 698ec6859b2STodor Tomov pix_mp->plane_fmt[i].sizeimage = pix_mp->height / 699ec6859b2STodor Tomov fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; 700ec6859b2STodor Tomov } 701ec6859b2STodor Tomov 702ec6859b2STodor Tomov pix_mp->field = V4L2_FIELD_NONE; 703ec6859b2STodor Tomov pix_mp->colorspace = V4L2_COLORSPACE_SRGB; 704ec6859b2STodor Tomov pix_mp->flags = 0; 705ec6859b2STodor Tomov pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace); 706ec6859b2STodor Tomov pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 707ec6859b2STodor Tomov pix_mp->colorspace, pix_mp->ycbcr_enc); 708ec6859b2STodor Tomov pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace); 709ec6859b2STodor Tomov 710ec6859b2STodor Tomov if (video->line_based) 711ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 712ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 713ec6859b2STodor Tomov p->bytesperline = clamp_t(u32, p->bytesperline, 714ec6859b2STodor Tomov 1, 65528); 715ec6859b2STodor Tomov p->sizeimage = clamp_t(u32, p->sizeimage, 716ec6859b2STodor Tomov p->bytesperline, 717ec6859b2STodor Tomov p->bytesperline * 4096); 718ec6859b2STodor Tomov lines = p->sizeimage / p->bytesperline; 719ec6859b2STodor Tomov 720ec6859b2STodor Tomov if (p->bytesperline < bytesperline[i]) 721ec6859b2STodor Tomov p->bytesperline = ALIGN(bytesperline[i], 8); 722ec6859b2STodor Tomov 723ec6859b2STodor Tomov if (p->sizeimage < p->bytesperline * lines) 724ec6859b2STodor Tomov p->sizeimage = p->bytesperline * lines; 725ec6859b2STodor Tomov 726ec6859b2STodor Tomov if (p->sizeimage < sizeimage[i]) 727ec6859b2STodor Tomov p->sizeimage = sizeimage[i]; 728ec6859b2STodor Tomov } 729ec6859b2STodor Tomov 730ec6859b2STodor Tomov return 0; 731ec6859b2STodor Tomov } 732ec6859b2STodor Tomov 733ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 734ec6859b2STodor Tomov { 735ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 736ec6859b2STodor Tomov 737ec6859b2STodor Tomov return __video_try_fmt(video, f); 738ec6859b2STodor Tomov } 739ec6859b2STodor Tomov 740ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 741ec6859b2STodor Tomov { 742ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 743ec6859b2STodor Tomov int ret; 744ec6859b2STodor Tomov 745ec6859b2STodor Tomov if (vb2_is_busy(&video->vb2_q)) 746ec6859b2STodor Tomov return -EBUSY; 747ec6859b2STodor Tomov 748ec6859b2STodor Tomov ret = __video_try_fmt(video, f); 749ec6859b2STodor Tomov if (ret < 0) 750ec6859b2STodor Tomov return ret; 751ec6859b2STodor Tomov 752ec6859b2STodor Tomov video->active_fmt = *f; 753ec6859b2STodor Tomov 754ec6859b2STodor Tomov return 0; 755ec6859b2STodor Tomov } 756ec6859b2STodor Tomov 757ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh, 758ec6859b2STodor Tomov struct v4l2_input *input) 759ec6859b2STodor Tomov { 760ec6859b2STodor Tomov if (input->index > 0) 761ec6859b2STodor Tomov return -EINVAL; 762ec6859b2STodor Tomov 763c0decac1SMauro Carvalho Chehab strscpy(input->name, "camera", sizeof(input->name)); 764ec6859b2STodor Tomov input->type = V4L2_INPUT_TYPE_CAMERA; 765ec6859b2STodor Tomov 766ec6859b2STodor Tomov return 0; 767ec6859b2STodor Tomov } 768ec6859b2STodor Tomov 769ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input) 770ec6859b2STodor Tomov { 771ec6859b2STodor Tomov *input = 0; 772ec6859b2STodor Tomov 773ec6859b2STodor Tomov return 0; 774ec6859b2STodor Tomov } 775ec6859b2STodor Tomov 776ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input) 777ec6859b2STodor Tomov { 778ec6859b2STodor Tomov return input == 0 ? 0 : -EINVAL; 779ec6859b2STodor Tomov } 780ec6859b2STodor Tomov 781ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { 782ec6859b2STodor Tomov .vidioc_querycap = video_querycap, 7837e98b7b5SBoris Brezillon .vidioc_enum_fmt_vid_cap = video_enum_fmt, 78435493d65SAndrey Konovalov .vidioc_enum_framesizes = video_enum_framesizes, 785ec6859b2STodor Tomov .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, 786ec6859b2STodor Tomov .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, 787ec6859b2STodor Tomov .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, 788ec6859b2STodor Tomov .vidioc_reqbufs = vb2_ioctl_reqbufs, 789ec6859b2STodor Tomov .vidioc_querybuf = vb2_ioctl_querybuf, 790ec6859b2STodor Tomov .vidioc_qbuf = vb2_ioctl_qbuf, 791ec6859b2STodor Tomov .vidioc_expbuf = vb2_ioctl_expbuf, 792ec6859b2STodor Tomov .vidioc_dqbuf = vb2_ioctl_dqbuf, 793ec6859b2STodor Tomov .vidioc_create_bufs = vb2_ioctl_create_bufs, 794ec6859b2STodor Tomov .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 795ec6859b2STodor Tomov .vidioc_streamon = vb2_ioctl_streamon, 796ec6859b2STodor Tomov .vidioc_streamoff = vb2_ioctl_streamoff, 797ec6859b2STodor Tomov .vidioc_enum_input = video_enum_input, 798ec6859b2STodor Tomov .vidioc_g_input = video_g_input, 799ec6859b2STodor Tomov .vidioc_s_input = video_s_input, 800ec6859b2STodor Tomov }; 801ec6859b2STodor Tomov 802ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 803ec6859b2STodor Tomov * V4L2 file operations 804ec6859b2STodor Tomov */ 805ec6859b2STodor Tomov 806ec6859b2STodor Tomov static int video_open(struct file *file) 807ec6859b2STodor Tomov { 808ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 809ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 810ec6859b2STodor Tomov struct v4l2_fh *vfh; 811ec6859b2STodor Tomov int ret; 812ec6859b2STodor Tomov 813ec6859b2STodor Tomov mutex_lock(&video->lock); 814ec6859b2STodor Tomov 815ec6859b2STodor Tomov vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); 816ec6859b2STodor Tomov if (vfh == NULL) { 817ec6859b2STodor Tomov ret = -ENOMEM; 818ec6859b2STodor Tomov goto error_alloc; 819ec6859b2STodor Tomov } 820ec6859b2STodor Tomov 821ec6859b2STodor Tomov v4l2_fh_init(vfh, vdev); 822ec6859b2STodor Tomov v4l2_fh_add(vfh); 823ec6859b2STodor Tomov 824ec6859b2STodor Tomov file->private_data = vfh; 825ec6859b2STodor Tomov 8268fd390b8SEzequiel Garcia ret = v4l2_pipeline_pm_get(&vdev->entity); 827ec6859b2STodor Tomov if (ret < 0) { 828ec6859b2STodor Tomov dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", 829ec6859b2STodor Tomov ret); 830ec6859b2STodor Tomov goto error_pm_use; 831ec6859b2STodor Tomov } 832ec6859b2STodor Tomov 833ec6859b2STodor Tomov mutex_unlock(&video->lock); 834ec6859b2STodor Tomov 835ec6859b2STodor Tomov return 0; 836ec6859b2STodor Tomov 837ec6859b2STodor Tomov error_pm_use: 838ec6859b2STodor Tomov v4l2_fh_release(file); 839ec6859b2STodor Tomov 840ec6859b2STodor Tomov error_alloc: 841ec6859b2STodor Tomov mutex_unlock(&video->lock); 842ec6859b2STodor Tomov 843ec6859b2STodor Tomov return ret; 844ec6859b2STodor Tomov } 845ec6859b2STodor Tomov 846ec6859b2STodor Tomov static int video_release(struct file *file) 847ec6859b2STodor Tomov { 848ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 849ec6859b2STodor Tomov 850ec6859b2STodor Tomov vb2_fop_release(file); 851ec6859b2STodor Tomov 8528fd390b8SEzequiel Garcia v4l2_pipeline_pm_put(&vdev->entity); 853ec6859b2STodor Tomov 854ec6859b2STodor Tomov file->private_data = NULL; 855ec6859b2STodor Tomov 856ec6859b2STodor Tomov return 0; 857ec6859b2STodor Tomov } 858ec6859b2STodor Tomov 859ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = { 860ec6859b2STodor Tomov .owner = THIS_MODULE, 861ec6859b2STodor Tomov .unlocked_ioctl = video_ioctl2, 862ec6859b2STodor Tomov .open = video_open, 863ec6859b2STodor Tomov .release = video_release, 864ec6859b2STodor Tomov .poll = vb2_fop_poll, 865ec6859b2STodor Tomov .mmap = vb2_fop_mmap, 866ec6859b2STodor Tomov .read = vb2_fop_read, 867ec6859b2STodor Tomov }; 868ec6859b2STodor Tomov 869ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 870ec6859b2STodor Tomov * CAMSS video core 871ec6859b2STodor Tomov */ 872ec6859b2STodor Tomov 873ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev) 874ec6859b2STodor Tomov { 875ec6859b2STodor Tomov struct camss_video *video = video_get_drvdata(vdev); 876ec6859b2STodor Tomov 877ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 878ec6859b2STodor Tomov 879ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 880ec6859b2STodor Tomov mutex_destroy(&video->lock); 881ec6859b2STodor Tomov 882ec6859b2STodor Tomov if (atomic_dec_and_test(&video->camss->ref_count)) 883ec6859b2STodor Tomov camss_delete(video->camss); 884ec6859b2STodor Tomov } 885ec6859b2STodor Tomov 886ec6859b2STodor Tomov /* 887ec6859b2STodor Tomov * msm_video_init_format - Helper function to initialize format 888ec6859b2STodor Tomov * @video: struct camss_video 889ec6859b2STodor Tomov * 890ec6859b2STodor Tomov * Initialize pad format with default value. 891ec6859b2STodor Tomov * 892ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 893ec6859b2STodor Tomov */ 894ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video) 895ec6859b2STodor Tomov { 896ec6859b2STodor Tomov int ret; 897ec6859b2STodor Tomov struct v4l2_format format = { 898ec6859b2STodor Tomov .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 899ec6859b2STodor Tomov .fmt.pix_mp = { 900ec6859b2STodor Tomov .width = 1920, 901ec6859b2STodor Tomov .height = 1080, 902ec6859b2STodor Tomov .pixelformat = video->formats[0].pixelformat, 903ec6859b2STodor Tomov }, 904ec6859b2STodor Tomov }; 905ec6859b2STodor Tomov 906ec6859b2STodor Tomov ret = __video_try_fmt(video, &format); 907ec6859b2STodor Tomov if (ret < 0) 908ec6859b2STodor Tomov return ret; 909ec6859b2STodor Tomov 910ec6859b2STodor Tomov video->active_fmt = format; 911ec6859b2STodor Tomov 912ec6859b2STodor Tomov return 0; 913ec6859b2STodor Tomov } 914ec6859b2STodor Tomov 915ec6859b2STodor Tomov /* 916ec6859b2STodor Tomov * msm_video_register - Register a video device node 917ec6859b2STodor Tomov * @video: struct camss_video 918ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 919ec6859b2STodor Tomov * @name: name to be used for the video device node 920ec6859b2STodor Tomov * 921ec6859b2STodor Tomov * Initialize and register a video device node to a V4L2 device. Also 922ec6859b2STodor Tomov * initialize the vb2 queue. 923ec6859b2STodor Tomov * 924ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 925ec6859b2STodor Tomov */ 926ec6859b2STodor Tomov 927ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, 928ec6859b2STodor Tomov const char *name, int is_pix) 929ec6859b2STodor Tomov { 930ec6859b2STodor Tomov struct media_pad *pad = &video->pad; 931ec6859b2STodor Tomov struct video_device *vdev; 932ec6859b2STodor Tomov struct vb2_queue *q; 933ec6859b2STodor Tomov int ret; 934ec6859b2STodor Tomov 935ec6859b2STodor Tomov vdev = &video->vdev; 936ec6859b2STodor Tomov 937ec6859b2STodor Tomov mutex_init(&video->q_lock); 938ec6859b2STodor Tomov 939ec6859b2STodor Tomov q = &video->vb2_q; 940ec6859b2STodor Tomov q->drv_priv = video; 941ec6859b2STodor Tomov q->mem_ops = &vb2_dma_sg_memops; 942ec6859b2STodor Tomov q->ops = &msm_video_vb2_q_ops; 943ec6859b2STodor Tomov q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 944ec6859b2STodor Tomov q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; 945ec6859b2STodor Tomov q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 946ec6859b2STodor Tomov q->buf_struct_size = sizeof(struct camss_buffer); 947ec6859b2STodor Tomov q->dev = video->camss->dev; 948ec6859b2STodor Tomov q->lock = &video->q_lock; 949ec6859b2STodor Tomov ret = vb2_queue_init(q); 950ec6859b2STodor Tomov if (ret < 0) { 951ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); 952ec6859b2STodor Tomov goto error_vb2_init; 953ec6859b2STodor Tomov } 954ec6859b2STodor Tomov 955ec6859b2STodor Tomov pad->flags = MEDIA_PAD_FL_SINK; 956ec6859b2STodor Tomov ret = media_entity_pads_init(&vdev->entity, 1, pad); 957ec6859b2STodor Tomov if (ret < 0) { 958ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", 959ec6859b2STodor Tomov ret); 960492abcd7SHans Verkuil goto error_vb2_init; 961ec6859b2STodor Tomov } 962ec6859b2STodor Tomov 963ec6859b2STodor Tomov mutex_init(&video->lock); 964ec6859b2STodor Tomov 965cba3819dSTodor Tomov if (video->camss->version == CAMSS_8x16) { 966ec6859b2STodor Tomov if (is_pix) { 967cba3819dSTodor Tomov video->formats = formats_pix_8x16; 968cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x16); 969cba3819dSTodor Tomov } else { 970cba3819dSTodor Tomov video->formats = formats_rdi_8x16; 971cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x16); 972cba3819dSTodor Tomov } 973cba3819dSTodor Tomov } else if (video->camss->version == CAMSS_8x96) { 974cba3819dSTodor Tomov if (is_pix) { 975cba3819dSTodor Tomov video->formats = formats_pix_8x96; 976cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x96); 977cba3819dSTodor Tomov } else { 978cba3819dSTodor Tomov video->formats = formats_rdi_8x96; 979cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x96); 980cba3819dSTodor Tomov } 981cba3819dSTodor Tomov } else { 982cba3819dSTodor Tomov goto error_video_register; 983ec6859b2STodor Tomov } 984ec6859b2STodor Tomov 985ec6859b2STodor Tomov ret = msm_video_init_format(video); 986ec6859b2STodor Tomov if (ret < 0) { 987ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); 988ec6859b2STodor Tomov goto error_video_register; 989ec6859b2STodor Tomov } 990ec6859b2STodor Tomov 991ec6859b2STodor Tomov vdev->fops = &msm_vid_fops; 992c90f1178SAndrey Konovalov vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING 993c90f1178SAndrey Konovalov | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; 994ec6859b2STodor Tomov vdev->ioctl_ops = &msm_vid_ioctl_ops; 995ec6859b2STodor Tomov vdev->release = msm_video_release; 996ec6859b2STodor Tomov vdev->v4l2_dev = v4l2_dev; 997ec6859b2STodor Tomov vdev->vfl_dir = VFL_DIR_RX; 998ec6859b2STodor Tomov vdev->queue = &video->vb2_q; 999ec6859b2STodor Tomov vdev->lock = &video->lock; 1000c0decac1SMauro Carvalho Chehab strscpy(vdev->name, name, sizeof(vdev->name)); 1001ec6859b2STodor Tomov 100270cad449SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 1003ec6859b2STodor Tomov if (ret < 0) { 1004ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", 1005ec6859b2STodor Tomov ret); 1006ec6859b2STodor Tomov goto error_video_register; 1007ec6859b2STodor Tomov } 1008ec6859b2STodor Tomov 1009ec6859b2STodor Tomov video_set_drvdata(vdev, video); 1010ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 1011ec6859b2STodor Tomov 1012ec6859b2STodor Tomov return 0; 1013ec6859b2STodor Tomov 1014ec6859b2STodor Tomov error_video_register: 1015ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 1016ec6859b2STodor Tomov mutex_destroy(&video->lock); 1017ec6859b2STodor Tomov error_vb2_init: 1018ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 1019ec6859b2STodor Tomov 1020ec6859b2STodor Tomov return ret; 1021ec6859b2STodor Tomov } 1022ec6859b2STodor Tomov 1023ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video) 1024ec6859b2STodor Tomov { 1025ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 1026492abcd7SHans Verkuil vb2_video_unregister_device(&video->vdev); 1027ec6859b2STodor Tomov atomic_dec(&video->camss->ref_count); 1028ec6859b2STodor Tomov } 1029