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