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 1367319cdf1SRobert Foss static const struct camss_format_info formats_rdi_845[] = { 1377319cdf1SRobert Foss { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, 1387319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1397319cdf1SRobert Foss { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, 1407319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1417319cdf1SRobert Foss { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, 1427319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1437319cdf1SRobert Foss { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, 1447319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1457319cdf1SRobert Foss { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, 1467319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 1477319cdf1SRobert Foss { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, 1487319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 1497319cdf1SRobert Foss { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, 1507319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 1517319cdf1SRobert Foss { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, 1527319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 1537319cdf1SRobert Foss { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, 1547319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1557319cdf1SRobert Foss { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, 1567319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1577319cdf1SRobert Foss { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, 1587319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1597319cdf1SRobert Foss { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, 1607319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1617319cdf1SRobert Foss { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, 1627319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1637319cdf1SRobert Foss { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, 1647319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 1657319cdf1SRobert Foss { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, 1667319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 1677319cdf1SRobert Foss { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, 1687319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 1697319cdf1SRobert Foss { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, 1707319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 12 } }, 1717319cdf1SRobert Foss { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, 1727319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 1737319cdf1SRobert Foss { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, 1747319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 1757319cdf1SRobert Foss { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, 1767319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 1777319cdf1SRobert Foss { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, 1787319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 14 } }, 179e53d6608SJonathan Marek { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1, 180e53d6608SJonathan Marek { { 1, 1 } }, { { 1, 1 } }, { 8 } }, 1817319cdf1SRobert Foss { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, 1827319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 10 } }, 1837319cdf1SRobert Foss { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, 1847319cdf1SRobert Foss { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 1857319cdf1SRobert Foss }; 1867319cdf1SRobert Foss 187cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x16[] = { 188cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, 189cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 190cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, 191cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 192cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 193cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 194cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 195cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 196cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, 197cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 198cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, 199cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 200cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 201cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 202cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 203cba3819dSTodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 204cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, 205cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 206cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, 207cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 208cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, 209cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 210cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, 211cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 212cba3819dSTodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, 213cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 214cba3819dSTodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, 215cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 216cba3819dSTodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, 217cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 218cba3819dSTodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, 219cba3819dSTodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 220cba3819dSTodor Tomov }; 221cba3819dSTodor Tomov 222cba3819dSTodor Tomov static const struct camss_format_info formats_pix_8x96[] = { 223ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, 224ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 225ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, 226ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 227ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 228ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 229ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, 230ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 231ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, 232ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 233ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, 234ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 235ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 236ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 237ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, 238ec6859b2STodor Tomov { { 1, 1 } }, { { 2, 3 } }, { 8 } }, 239ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, 240ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 241ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, 242ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 243ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, 244ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 245ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, 246ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 247ec6859b2STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, 248ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 249ec6859b2STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, 250ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 251ec6859b2STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, 252ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 253ec6859b2STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, 254ec6859b2STodor Tomov { { 1, 1 } }, { { 1, 2 } }, { 8 } }, 255312e1c85STodor Tomov { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, 256312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 257312e1c85STodor Tomov { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, 258312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 259312e1c85STodor Tomov { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, 260312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 261312e1c85STodor Tomov { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, 262312e1c85STodor Tomov { { 1, 1 } }, { { 1, 1 } }, { 16 } }, 263ec6859b2STodor Tomov }; 264ec6859b2STodor Tomov 265ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 266ec6859b2STodor Tomov * Helper functions 267ec6859b2STodor Tomov */ 268ec6859b2STodor Tomov 269ec6859b2STodor Tomov static int video_find_format(u32 code, u32 pixelformat, 270ec6859b2STodor Tomov const struct camss_format_info *formats, 271ec6859b2STodor Tomov unsigned int nformats) 272ec6859b2STodor Tomov { 273ec6859b2STodor Tomov int i; 274ec6859b2STodor Tomov 275ec6859b2STodor Tomov for (i = 0; i < nformats; i++) { 276ec6859b2STodor Tomov if (formats[i].code == code && 277ec6859b2STodor Tomov formats[i].pixelformat == pixelformat) 278ec6859b2STodor Tomov return i; 279ec6859b2STodor Tomov } 280ec6859b2STodor Tomov 281ec6859b2STodor Tomov for (i = 0; i < nformats; i++) 282ec6859b2STodor Tomov if (formats[i].code == code) 283ec6859b2STodor Tomov return i; 284ec6859b2STodor Tomov 285ec6859b2STodor Tomov WARN_ON(1); 286ec6859b2STodor Tomov 287ec6859b2STodor Tomov return -EINVAL; 288ec6859b2STodor Tomov } 289ec6859b2STodor Tomov 290ec6859b2STodor Tomov /* 291ec6859b2STodor Tomov * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane 292ec6859b2STodor Tomov * @mbus: v4l2_mbus_framefmt format (input) 293ec6859b2STodor Tomov * @pix: v4l2_pix_format_mplane format (output) 294ec6859b2STodor Tomov * @f: a pointer to formats array element to be used for the conversion 295ec6859b2STodor Tomov * @alignment: bytesperline alignment value 296ec6859b2STodor Tomov * 297ec6859b2STodor Tomov * Fill the output pix structure with information from the input mbus format. 298ec6859b2STodor Tomov * 299ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 300ec6859b2STodor Tomov */ 301ec6859b2STodor Tomov static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, 302ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix, 303ec6859b2STodor Tomov const struct camss_format_info *f, 304ec6859b2STodor Tomov unsigned int alignment) 305ec6859b2STodor Tomov { 306ec6859b2STodor Tomov unsigned int i; 307ec6859b2STodor Tomov u32 bytesperline; 308ec6859b2STodor Tomov 309ec6859b2STodor Tomov memset(pix, 0, sizeof(*pix)); 310ec6859b2STodor Tomov v4l2_fill_pix_format_mplane(pix, mbus); 311ec6859b2STodor Tomov pix->pixelformat = f->pixelformat; 312ec6859b2STodor Tomov pix->num_planes = f->planes; 313ec6859b2STodor Tomov for (i = 0; i < pix->num_planes; i++) { 314ec6859b2STodor Tomov bytesperline = pix->width / f->hsub[i].numerator * 315ec6859b2STodor Tomov f->hsub[i].denominator * f->bpp[i] / 8; 316ec6859b2STodor Tomov bytesperline = ALIGN(bytesperline, alignment); 317ec6859b2STodor Tomov pix->plane_fmt[i].bytesperline = bytesperline; 318ec6859b2STodor Tomov pix->plane_fmt[i].sizeimage = pix->height / 319ec6859b2STodor Tomov f->vsub[i].numerator * f->vsub[i].denominator * 320ec6859b2STodor Tomov bytesperline; 321ec6859b2STodor Tomov } 322ec6859b2STodor Tomov 323ec6859b2STodor Tomov return 0; 324ec6859b2STodor Tomov } 325ec6859b2STodor Tomov 326ec6859b2STodor Tomov static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, 327ec6859b2STodor Tomov u32 *pad) 328ec6859b2STodor Tomov { 329ec6859b2STodor Tomov struct media_pad *remote; 330ec6859b2STodor Tomov 331ec6859b2STodor Tomov remote = media_entity_remote_pad(&video->pad); 332ec6859b2STodor Tomov 333ec6859b2STodor Tomov if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 334ec6859b2STodor Tomov return NULL; 335ec6859b2STodor Tomov 336ec6859b2STodor Tomov if (pad) 337ec6859b2STodor Tomov *pad = remote->index; 338ec6859b2STodor Tomov 339ec6859b2STodor Tomov return media_entity_to_v4l2_subdev(remote->entity); 340ec6859b2STodor Tomov } 341ec6859b2STodor Tomov 342ec6859b2STodor Tomov static int video_get_subdev_format(struct camss_video *video, 343ec6859b2STodor Tomov struct v4l2_format *format) 344ec6859b2STodor Tomov { 345ec6859b2STodor Tomov struct v4l2_subdev_format fmt; 346ec6859b2STodor Tomov struct v4l2_subdev *subdev; 347ec6859b2STodor Tomov u32 pad; 348ec6859b2STodor Tomov int ret; 349ec6859b2STodor Tomov 350ec6859b2STodor Tomov subdev = video_remote_subdev(video, &pad); 351ec6859b2STodor Tomov if (subdev == NULL) 352ec6859b2STodor Tomov return -EPIPE; 353ec6859b2STodor Tomov 354ec6859b2STodor Tomov fmt.pad = pad; 355ec6859b2STodor Tomov fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 356ec6859b2STodor Tomov 357ec6859b2STodor Tomov ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 358ec6859b2STodor Tomov if (ret) 359ec6859b2STodor Tomov return ret; 360ec6859b2STodor Tomov 361ec6859b2STodor Tomov ret = video_find_format(fmt.format.code, 362ec6859b2STodor Tomov format->fmt.pix_mp.pixelformat, 363ec6859b2STodor Tomov video->formats, video->nformats); 364ec6859b2STodor Tomov if (ret < 0) 365ec6859b2STodor Tomov return ret; 366ec6859b2STodor Tomov 367ec6859b2STodor Tomov format->type = video->type; 368ec6859b2STodor Tomov 369ec6859b2STodor Tomov return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, 370ec6859b2STodor Tomov &video->formats[ret], video->bpl_alignment); 371ec6859b2STodor Tomov } 372ec6859b2STodor Tomov 373ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 374ec6859b2STodor Tomov * Video queue operations 375ec6859b2STodor Tomov */ 376ec6859b2STodor Tomov 377ec6859b2STodor Tomov static int video_queue_setup(struct vb2_queue *q, 378ec6859b2STodor Tomov unsigned int *num_buffers, unsigned int *num_planes, 379ec6859b2STodor Tomov unsigned int sizes[], struct device *alloc_devs[]) 380ec6859b2STodor Tomov { 381ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 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 if (*num_planes) { 387ec6859b2STodor Tomov if (*num_planes != format->num_planes) 388ec6859b2STodor Tomov return -EINVAL; 389ec6859b2STodor Tomov 390ec6859b2STodor Tomov for (i = 0; i < *num_planes; i++) 391ec6859b2STodor Tomov if (sizes[i] < format->plane_fmt[i].sizeimage) 392ec6859b2STodor Tomov return -EINVAL; 393ec6859b2STodor Tomov 394ec6859b2STodor Tomov return 0; 395ec6859b2STodor Tomov } 396ec6859b2STodor Tomov 397ec6859b2STodor Tomov *num_planes = format->num_planes; 398ec6859b2STodor Tomov 399ec6859b2STodor Tomov for (i = 0; i < *num_planes; i++) 400ec6859b2STodor Tomov sizes[i] = format->plane_fmt[i].sizeimage; 401ec6859b2STodor Tomov 402ec6859b2STodor Tomov return 0; 403ec6859b2STodor Tomov } 404ec6859b2STodor Tomov 405ec6859b2STodor Tomov static int video_buf_init(struct vb2_buffer *vb) 406ec6859b2STodor Tomov { 407ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 408ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 409ec6859b2STodor Tomov struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, 410ec6859b2STodor Tomov vb); 411ec6859b2STodor Tomov const struct v4l2_pix_format_mplane *format = 412ec6859b2STodor Tomov &video->active_fmt.fmt.pix_mp; 413ec6859b2STodor Tomov struct sg_table *sgt; 414ec6859b2STodor Tomov unsigned int i; 415ec6859b2STodor Tomov 416ec6859b2STodor Tomov for (i = 0; i < format->num_planes; i++) { 417ec6859b2STodor Tomov sgt = vb2_dma_sg_plane_desc(vb, i); 418ec6859b2STodor Tomov if (!sgt) 419ec6859b2STodor Tomov return -EFAULT; 420ec6859b2STodor Tomov 421ec6859b2STodor Tomov buffer->addr[i] = sg_dma_address(sgt->sgl); 422ec6859b2STodor Tomov } 423ec6859b2STodor Tomov 424ec6859b2STodor Tomov if (format->pixelformat == V4L2_PIX_FMT_NV12 || 425ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV21 || 426ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV16 || 427ec6859b2STodor Tomov format->pixelformat == V4L2_PIX_FMT_NV61) 428ec6859b2STodor Tomov buffer->addr[1] = buffer->addr[0] + 429ec6859b2STodor Tomov format->plane_fmt[0].bytesperline * 430ec6859b2STodor Tomov format->height; 431ec6859b2STodor Tomov 432ec6859b2STodor Tomov return 0; 433ec6859b2STodor Tomov } 434ec6859b2STodor Tomov 435ec6859b2STodor Tomov static int video_buf_prepare(struct vb2_buffer *vb) 436ec6859b2STodor Tomov { 437ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 438ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 439ec6859b2STodor Tomov const struct v4l2_pix_format_mplane *format = 440ec6859b2STodor Tomov &video->active_fmt.fmt.pix_mp; 441ec6859b2STodor Tomov unsigned int i; 442ec6859b2STodor Tomov 443ec6859b2STodor Tomov for (i = 0; i < format->num_planes; i++) { 444ec6859b2STodor Tomov if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i)) 445ec6859b2STodor Tomov return -EINVAL; 446ec6859b2STodor Tomov 447ec6859b2STodor Tomov vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage); 448ec6859b2STodor Tomov } 449ec6859b2STodor Tomov 450ec6859b2STodor Tomov vbuf->field = V4L2_FIELD_NONE; 451ec6859b2STodor Tomov 452ec6859b2STodor Tomov return 0; 453ec6859b2STodor Tomov } 454ec6859b2STodor Tomov 455ec6859b2STodor Tomov static void video_buf_queue(struct vb2_buffer *vb) 456ec6859b2STodor Tomov { 457ec6859b2STodor Tomov struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 458ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue); 459ec6859b2STodor Tomov struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, 460ec6859b2STodor Tomov vb); 461ec6859b2STodor Tomov 462ec6859b2STodor Tomov video->ops->queue_buffer(video, buffer); 463ec6859b2STodor Tomov } 464ec6859b2STodor Tomov 465ec6859b2STodor Tomov static int video_check_format(struct camss_video *video) 466ec6859b2STodor Tomov { 467ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp; 468ec6859b2STodor Tomov struct v4l2_format format; 469ec6859b2STodor Tomov struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp; 470ec6859b2STodor Tomov int ret; 471ec6859b2STodor Tomov 472ec6859b2STodor Tomov sd_pix->pixelformat = pix->pixelformat; 473ec6859b2STodor Tomov ret = video_get_subdev_format(video, &format); 474ec6859b2STodor Tomov if (ret < 0) 475ec6859b2STodor Tomov return ret; 476ec6859b2STodor Tomov 477ec6859b2STodor Tomov if (pix->pixelformat != sd_pix->pixelformat || 478ec6859b2STodor Tomov pix->height != sd_pix->height || 479ec6859b2STodor Tomov pix->width != sd_pix->width || 480ec6859b2STodor Tomov pix->num_planes != sd_pix->num_planes || 481ec6859b2STodor Tomov pix->field != format.fmt.pix_mp.field) 482ec6859b2STodor Tomov return -EPIPE; 483ec6859b2STodor Tomov 484ec6859b2STodor Tomov return 0; 485ec6859b2STodor Tomov } 486ec6859b2STodor Tomov 487ec6859b2STodor Tomov static int video_start_streaming(struct vb2_queue *q, unsigned int count) 488ec6859b2STodor Tomov { 489ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 490ec6859b2STodor Tomov struct video_device *vdev = &video->vdev; 491ec6859b2STodor Tomov struct media_entity *entity; 492ec6859b2STodor Tomov struct media_pad *pad; 493ec6859b2STodor Tomov struct v4l2_subdev *subdev; 494ec6859b2STodor Tomov int ret; 495ec6859b2STodor Tomov 496ec6859b2STodor Tomov ret = media_pipeline_start(&vdev->entity, &video->pipe); 497ec6859b2STodor Tomov if (ret < 0) 498ec6859b2STodor Tomov return ret; 499ec6859b2STodor Tomov 500ec6859b2STodor Tomov ret = video_check_format(video); 501ec6859b2STodor Tomov if (ret < 0) 502ec6859b2STodor Tomov goto error; 503ec6859b2STodor Tomov 504ec6859b2STodor Tomov entity = &vdev->entity; 505ec6859b2STodor Tomov while (1) { 506ec6859b2STodor Tomov pad = &entity->pads[0]; 507ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 508ec6859b2STodor Tomov break; 509ec6859b2STodor Tomov 510ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 511ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 512ec6859b2STodor Tomov break; 513ec6859b2STodor Tomov 514ec6859b2STodor Tomov entity = pad->entity; 515ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(entity); 516ec6859b2STodor Tomov 517ec6859b2STodor Tomov ret = v4l2_subdev_call(subdev, video, s_stream, 1); 518ec6859b2STodor Tomov if (ret < 0 && ret != -ENOIOCTLCMD) 519ec6859b2STodor Tomov goto error; 520ec6859b2STodor Tomov } 521ec6859b2STodor Tomov 522ec6859b2STodor Tomov return 0; 523ec6859b2STodor Tomov 524ec6859b2STodor Tomov error: 525ec6859b2STodor Tomov media_pipeline_stop(&vdev->entity); 526ec6859b2STodor Tomov 527ec6859b2STodor Tomov video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); 528ec6859b2STodor Tomov 529ec6859b2STodor Tomov return ret; 530ec6859b2STodor Tomov } 531ec6859b2STodor Tomov 532ec6859b2STodor Tomov static void video_stop_streaming(struct vb2_queue *q) 533ec6859b2STodor Tomov { 534ec6859b2STodor Tomov struct camss_video *video = vb2_get_drv_priv(q); 535ec6859b2STodor Tomov struct video_device *vdev = &video->vdev; 536ec6859b2STodor Tomov struct media_entity *entity; 537ec6859b2STodor Tomov struct media_pad *pad; 538ec6859b2STodor Tomov struct v4l2_subdev *subdev; 539ec6859b2STodor Tomov 540ec6859b2STodor Tomov entity = &vdev->entity; 541ec6859b2STodor Tomov while (1) { 542ec6859b2STodor Tomov pad = &entity->pads[0]; 543ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 544ec6859b2STodor Tomov break; 545ec6859b2STodor Tomov 546ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 547ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 548ec6859b2STodor Tomov break; 549ec6859b2STodor Tomov 550ec6859b2STodor Tomov entity = pad->entity; 551ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(entity); 552ec6859b2STodor Tomov 553ec6859b2STodor Tomov v4l2_subdev_call(subdev, video, s_stream, 0); 554ec6859b2STodor Tomov } 555ec6859b2STodor Tomov 556ec6859b2STodor Tomov media_pipeline_stop(&vdev->entity); 557ec6859b2STodor Tomov 558ec6859b2STodor Tomov video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); 559ec6859b2STodor Tomov } 560ec6859b2STodor Tomov 561ec6859b2STodor Tomov static const struct vb2_ops msm_video_vb2_q_ops = { 562ec6859b2STodor Tomov .queue_setup = video_queue_setup, 563ec6859b2STodor Tomov .wait_prepare = vb2_ops_wait_prepare, 564ec6859b2STodor Tomov .wait_finish = vb2_ops_wait_finish, 565ec6859b2STodor Tomov .buf_init = video_buf_init, 566ec6859b2STodor Tomov .buf_prepare = video_buf_prepare, 567ec6859b2STodor Tomov .buf_queue = video_buf_queue, 568ec6859b2STodor Tomov .start_streaming = video_start_streaming, 569ec6859b2STodor Tomov .stop_streaming = video_stop_streaming, 570ec6859b2STodor Tomov }; 571ec6859b2STodor Tomov 572ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 573ec6859b2STodor Tomov * V4L2 ioctls 574ec6859b2STodor Tomov */ 575ec6859b2STodor Tomov 576ec6859b2STodor Tomov static int video_querycap(struct file *file, void *fh, 577ec6859b2STodor Tomov struct v4l2_capability *cap) 578ec6859b2STodor Tomov { 579ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 580ec6859b2STodor Tomov 581c0decac1SMauro Carvalho Chehab strscpy(cap->driver, "qcom-camss", sizeof(cap->driver)); 582c0decac1SMauro Carvalho Chehab strscpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card)); 583ec6859b2STodor Tomov snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 584ec6859b2STodor Tomov dev_name(video->camss->dev)); 585ec6859b2STodor Tomov 586ec6859b2STodor Tomov return 0; 587ec6859b2STodor Tomov } 588ec6859b2STodor Tomov 589a3d412d4SAndrey Konovalov static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 590ec6859b2STodor Tomov { 591a3d412d4SAndrey Konovalov struct camss_video *video = video_drvdata(file); 592ec6859b2STodor Tomov int i, j, k; 593dfb5d328SAndrey Konovalov u32 mcode = f->mbus_code; 594ec6859b2STodor Tomov 595a3d412d4SAndrey Konovalov if (f->type != video->type) 596a3d412d4SAndrey Konovalov return -EINVAL; 597a3d412d4SAndrey Konovalov 598a3d412d4SAndrey Konovalov if (f->index >= video->nformats) 599a3d412d4SAndrey Konovalov return -EINVAL; 600a3d412d4SAndrey Konovalov 601dfb5d328SAndrey Konovalov /* 602dfb5d328SAndrey Konovalov * Find index "i" of "k"th unique pixelformat in formats array. 603dfb5d328SAndrey Konovalov * 604dfb5d328SAndrey Konovalov * If f->mbus_code passed to video_enum_fmt() is not zero, a device 605dfb5d328SAndrey Konovalov * with V4L2_CAP_IO_MC capability restricts enumeration to only the 606dfb5d328SAndrey Konovalov * pixel formats that can be produced from that media bus code. 607dfb5d328SAndrey Konovalov * This is implemented by skipping video->formats[] entries with 608dfb5d328SAndrey Konovalov * code != f->mbus_code (if f->mbus_code is not zero). 609dfb5d328SAndrey Konovalov * If the f->mbus_code passed to video_enum_fmt() is not supported, 610dfb5d328SAndrey Konovalov * -EINVAL is returned. 611dfb5d328SAndrey Konovalov * If f->mbus_code is zero, all the pixel formats are enumerated. 612dfb5d328SAndrey Konovalov */ 613ec6859b2STodor Tomov k = -1; 614ec6859b2STodor Tomov for (i = 0; i < video->nformats; i++) { 615dfb5d328SAndrey Konovalov if (mcode != 0 && video->formats[i].code != mcode) 616dfb5d328SAndrey Konovalov continue; 617dfb5d328SAndrey Konovalov 618ec6859b2STodor Tomov for (j = 0; j < i; j++) { 619dfb5d328SAndrey Konovalov if (mcode != 0 && video->formats[j].code != mcode) 620dfb5d328SAndrey Konovalov continue; 621ec6859b2STodor Tomov if (video->formats[i].pixelformat == 622ec6859b2STodor Tomov video->formats[j].pixelformat) 623ec6859b2STodor Tomov break; 624ec6859b2STodor Tomov } 625ec6859b2STodor Tomov 626ec6859b2STodor Tomov if (j == i) 627ec6859b2STodor Tomov k++; 628ec6859b2STodor Tomov 629a3d412d4SAndrey Konovalov if (k == f->index) 630a3d412d4SAndrey Konovalov break; 631ec6859b2STodor Tomov } 632ec6859b2STodor Tomov 633b00481bdSDan Carpenter if (k == -1 || k < f->index) 634dfb5d328SAndrey Konovalov /* 635dfb5d328SAndrey Konovalov * All the unique pixel formats matching the arguments 636dfb5d328SAndrey Konovalov * have been enumerated (k >= 0 and f->index > 0), or 637dfb5d328SAndrey Konovalov * no pixel formats match the non-zero f->mbus_code (k == -1). 638dfb5d328SAndrey Konovalov */ 639ec6859b2STodor Tomov return -EINVAL; 640ec6859b2STodor Tomov 641ec6859b2STodor Tomov f->pixelformat = video->formats[i].pixelformat; 642ec6859b2STodor Tomov 643ec6859b2STodor Tomov return 0; 644ec6859b2STodor Tomov } 645ec6859b2STodor Tomov 64635493d65SAndrey Konovalov static int video_enum_framesizes(struct file *file, void *fh, 64735493d65SAndrey Konovalov struct v4l2_frmsizeenum *fsize) 64835493d65SAndrey Konovalov { 64935493d65SAndrey Konovalov struct camss_video *video = video_drvdata(file); 65035493d65SAndrey Konovalov int i; 65135493d65SAndrey Konovalov 65235493d65SAndrey Konovalov if (fsize->index) 65335493d65SAndrey Konovalov return -EINVAL; 65435493d65SAndrey Konovalov 65535493d65SAndrey Konovalov /* Only accept pixel format present in the formats[] table */ 65635493d65SAndrey Konovalov for (i = 0; i < video->nformats; i++) { 65735493d65SAndrey Konovalov if (video->formats[i].pixelformat == fsize->pixel_format) 65835493d65SAndrey Konovalov break; 65935493d65SAndrey Konovalov } 66035493d65SAndrey Konovalov 66135493d65SAndrey Konovalov if (i == video->nformats) 66235493d65SAndrey Konovalov return -EINVAL; 66335493d65SAndrey Konovalov 66435493d65SAndrey Konovalov fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 66535493d65SAndrey Konovalov fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH; 66635493d65SAndrey Konovalov fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH; 66735493d65SAndrey Konovalov fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT; 66835493d65SAndrey Konovalov fsize->stepwise.max_height = (video->line_based) ? 66935493d65SAndrey Konovalov CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI; 67035493d65SAndrey Konovalov fsize->stepwise.step_width = 1; 67135493d65SAndrey Konovalov fsize->stepwise.step_height = 1; 67235493d65SAndrey Konovalov 67335493d65SAndrey Konovalov return 0; 67435493d65SAndrey Konovalov } 67535493d65SAndrey Konovalov 676ec6859b2STodor Tomov static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 677ec6859b2STodor Tomov { 678ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 679ec6859b2STodor Tomov 680ec6859b2STodor Tomov *f = video->active_fmt; 681ec6859b2STodor Tomov 682ec6859b2STodor Tomov return 0; 683ec6859b2STodor Tomov } 684ec6859b2STodor Tomov 685ec6859b2STodor Tomov static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 686ec6859b2STodor Tomov { 687ec6859b2STodor Tomov struct v4l2_pix_format_mplane *pix_mp; 688ec6859b2STodor Tomov const struct camss_format_info *fi; 689ec6859b2STodor Tomov struct v4l2_plane_pix_format *p; 690ec6859b2STodor Tomov u32 bytesperline[3] = { 0 }; 691ec6859b2STodor Tomov u32 sizeimage[3] = { 0 }; 692ec6859b2STodor Tomov u32 width, height; 693ec6859b2STodor Tomov u32 bpl, lines; 694ec6859b2STodor Tomov int i, j; 695ec6859b2STodor Tomov 696ec6859b2STodor Tomov pix_mp = &f->fmt.pix_mp; 697ec6859b2STodor Tomov 698ec6859b2STodor Tomov if (video->line_based) 699ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes && i < 3; i++) { 700ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 701ec6859b2STodor Tomov bytesperline[i] = clamp_t(u32, p->bytesperline, 702ec6859b2STodor Tomov 1, 65528); 703ec6859b2STodor Tomov sizeimage[i] = clamp_t(u32, p->sizeimage, 704ec6859b2STodor Tomov bytesperline[i], 705daf2298bSAndrey Konovalov bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX); 706ec6859b2STodor Tomov } 707ec6859b2STodor Tomov 708ec6859b2STodor Tomov for (j = 0; j < video->nformats; j++) 709ec6859b2STodor Tomov if (pix_mp->pixelformat == video->formats[j].pixelformat) 710ec6859b2STodor Tomov break; 711ec6859b2STodor Tomov 712ec6859b2STodor Tomov if (j == video->nformats) 713ec6859b2STodor Tomov j = 0; /* default format */ 714ec6859b2STodor Tomov 715ec6859b2STodor Tomov fi = &video->formats[j]; 716ec6859b2STodor Tomov width = pix_mp->width; 717ec6859b2STodor Tomov height = pix_mp->height; 718ec6859b2STodor Tomov 719ec6859b2STodor Tomov memset(pix_mp, 0, sizeof(*pix_mp)); 720ec6859b2STodor Tomov 721ec6859b2STodor Tomov pix_mp->pixelformat = fi->pixelformat; 722daf2298bSAndrey Konovalov pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH); 723daf2298bSAndrey Konovalov pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI); 724ec6859b2STodor Tomov pix_mp->num_planes = fi->planes; 725ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 726ec6859b2STodor Tomov bpl = pix_mp->width / fi->hsub[i].numerator * 727ec6859b2STodor Tomov fi->hsub[i].denominator * fi->bpp[i] / 8; 728ec6859b2STodor Tomov bpl = ALIGN(bpl, video->bpl_alignment); 729ec6859b2STodor Tomov pix_mp->plane_fmt[i].bytesperline = bpl; 730ec6859b2STodor Tomov pix_mp->plane_fmt[i].sizeimage = pix_mp->height / 731ec6859b2STodor Tomov fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; 732ec6859b2STodor Tomov } 733ec6859b2STodor Tomov 734ec6859b2STodor Tomov pix_mp->field = V4L2_FIELD_NONE; 735ec6859b2STodor Tomov pix_mp->colorspace = V4L2_COLORSPACE_SRGB; 736ec6859b2STodor Tomov pix_mp->flags = 0; 737ec6859b2STodor Tomov pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace); 738ec6859b2STodor Tomov pix_mp->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, 739ec6859b2STodor Tomov pix_mp->colorspace, pix_mp->ycbcr_enc); 740ec6859b2STodor Tomov pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace); 741ec6859b2STodor Tomov 742ec6859b2STodor Tomov if (video->line_based) 743ec6859b2STodor Tomov for (i = 0; i < pix_mp->num_planes; i++) { 744ec6859b2STodor Tomov p = &pix_mp->plane_fmt[i]; 745ec6859b2STodor Tomov p->bytesperline = clamp_t(u32, p->bytesperline, 746ec6859b2STodor Tomov 1, 65528); 747ec6859b2STodor Tomov p->sizeimage = clamp_t(u32, p->sizeimage, 748ec6859b2STodor Tomov p->bytesperline, 749daf2298bSAndrey Konovalov p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX); 750ec6859b2STodor Tomov lines = p->sizeimage / p->bytesperline; 751ec6859b2STodor Tomov 752ec6859b2STodor Tomov if (p->bytesperline < bytesperline[i]) 753ec6859b2STodor Tomov p->bytesperline = ALIGN(bytesperline[i], 8); 754ec6859b2STodor Tomov 755ec6859b2STodor Tomov if (p->sizeimage < p->bytesperline * lines) 756ec6859b2STodor Tomov p->sizeimage = p->bytesperline * lines; 757ec6859b2STodor Tomov 758ec6859b2STodor Tomov if (p->sizeimage < sizeimage[i]) 759ec6859b2STodor Tomov p->sizeimage = sizeimage[i]; 760ec6859b2STodor Tomov } 761ec6859b2STodor Tomov 762ec6859b2STodor Tomov return 0; 763ec6859b2STodor Tomov } 764ec6859b2STodor Tomov 765ec6859b2STodor Tomov static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 766ec6859b2STodor Tomov { 767ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 768ec6859b2STodor Tomov 769ec6859b2STodor Tomov return __video_try_fmt(video, f); 770ec6859b2STodor Tomov } 771ec6859b2STodor Tomov 772ec6859b2STodor Tomov static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 773ec6859b2STodor Tomov { 774ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 775ec6859b2STodor Tomov int ret; 776ec6859b2STodor Tomov 777ec6859b2STodor Tomov if (vb2_is_busy(&video->vb2_q)) 778ec6859b2STodor Tomov return -EBUSY; 779ec6859b2STodor Tomov 780ec6859b2STodor Tomov ret = __video_try_fmt(video, f); 781ec6859b2STodor Tomov if (ret < 0) 782ec6859b2STodor Tomov return ret; 783ec6859b2STodor Tomov 784ec6859b2STodor Tomov video->active_fmt = *f; 785ec6859b2STodor Tomov 786ec6859b2STodor Tomov return 0; 787ec6859b2STodor Tomov } 788ec6859b2STodor Tomov 789ec6859b2STodor Tomov static int video_enum_input(struct file *file, void *fh, 790ec6859b2STodor Tomov struct v4l2_input *input) 791ec6859b2STodor Tomov { 792ec6859b2STodor Tomov if (input->index > 0) 793ec6859b2STodor Tomov return -EINVAL; 794ec6859b2STodor Tomov 795c0decac1SMauro Carvalho Chehab strscpy(input->name, "camera", sizeof(input->name)); 796ec6859b2STodor Tomov input->type = V4L2_INPUT_TYPE_CAMERA; 797ec6859b2STodor Tomov 798ec6859b2STodor Tomov return 0; 799ec6859b2STodor Tomov } 800ec6859b2STodor Tomov 801ec6859b2STodor Tomov static int video_g_input(struct file *file, void *fh, unsigned int *input) 802ec6859b2STodor Tomov { 803ec6859b2STodor Tomov *input = 0; 804ec6859b2STodor Tomov 805ec6859b2STodor Tomov return 0; 806ec6859b2STodor Tomov } 807ec6859b2STodor Tomov 808ec6859b2STodor Tomov static int video_s_input(struct file *file, void *fh, unsigned int input) 809ec6859b2STodor Tomov { 810ec6859b2STodor Tomov return input == 0 ? 0 : -EINVAL; 811ec6859b2STodor Tomov } 812ec6859b2STodor Tomov 813ec6859b2STodor Tomov static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { 814ec6859b2STodor Tomov .vidioc_querycap = video_querycap, 8157e98b7b5SBoris Brezillon .vidioc_enum_fmt_vid_cap = video_enum_fmt, 81635493d65SAndrey Konovalov .vidioc_enum_framesizes = video_enum_framesizes, 817ec6859b2STodor Tomov .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, 818ec6859b2STodor Tomov .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, 819ec6859b2STodor Tomov .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, 820ec6859b2STodor Tomov .vidioc_reqbufs = vb2_ioctl_reqbufs, 821ec6859b2STodor Tomov .vidioc_querybuf = vb2_ioctl_querybuf, 822ec6859b2STodor Tomov .vidioc_qbuf = vb2_ioctl_qbuf, 823ec6859b2STodor Tomov .vidioc_expbuf = vb2_ioctl_expbuf, 824ec6859b2STodor Tomov .vidioc_dqbuf = vb2_ioctl_dqbuf, 825ec6859b2STodor Tomov .vidioc_create_bufs = vb2_ioctl_create_bufs, 826ec6859b2STodor Tomov .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 827ec6859b2STodor Tomov .vidioc_streamon = vb2_ioctl_streamon, 828ec6859b2STodor Tomov .vidioc_streamoff = vb2_ioctl_streamoff, 829ec6859b2STodor Tomov .vidioc_enum_input = video_enum_input, 830ec6859b2STodor Tomov .vidioc_g_input = video_g_input, 831ec6859b2STodor Tomov .vidioc_s_input = video_s_input, 832ec6859b2STodor Tomov }; 833ec6859b2STodor Tomov 834ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 835ec6859b2STodor Tomov * V4L2 file operations 836ec6859b2STodor Tomov */ 837ec6859b2STodor Tomov 838ec6859b2STodor Tomov static int video_open(struct file *file) 839ec6859b2STodor Tomov { 840ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 841ec6859b2STodor Tomov struct camss_video *video = video_drvdata(file); 842ec6859b2STodor Tomov struct v4l2_fh *vfh; 843ec6859b2STodor Tomov int ret; 844ec6859b2STodor Tomov 845ec6859b2STodor Tomov mutex_lock(&video->lock); 846ec6859b2STodor Tomov 847ec6859b2STodor Tomov vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); 848ec6859b2STodor Tomov if (vfh == NULL) { 849ec6859b2STodor Tomov ret = -ENOMEM; 850ec6859b2STodor Tomov goto error_alloc; 851ec6859b2STodor Tomov } 852ec6859b2STodor Tomov 853ec6859b2STodor Tomov v4l2_fh_init(vfh, vdev); 854ec6859b2STodor Tomov v4l2_fh_add(vfh); 855ec6859b2STodor Tomov 856ec6859b2STodor Tomov file->private_data = vfh; 857ec6859b2STodor Tomov 8588fd390b8SEzequiel Garcia ret = v4l2_pipeline_pm_get(&vdev->entity); 859ec6859b2STodor Tomov if (ret < 0) { 860ec6859b2STodor Tomov dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", 861ec6859b2STodor Tomov ret); 862ec6859b2STodor Tomov goto error_pm_use; 863ec6859b2STodor Tomov } 864ec6859b2STodor Tomov 865ec6859b2STodor Tomov mutex_unlock(&video->lock); 866ec6859b2STodor Tomov 867ec6859b2STodor Tomov return 0; 868ec6859b2STodor Tomov 869ec6859b2STodor Tomov error_pm_use: 870ec6859b2STodor Tomov v4l2_fh_release(file); 871ec6859b2STodor Tomov 872ec6859b2STodor Tomov error_alloc: 873ec6859b2STodor Tomov mutex_unlock(&video->lock); 874ec6859b2STodor Tomov 875ec6859b2STodor Tomov return ret; 876ec6859b2STodor Tomov } 877ec6859b2STodor Tomov 878ec6859b2STodor Tomov static int video_release(struct file *file) 879ec6859b2STodor Tomov { 880ec6859b2STodor Tomov struct video_device *vdev = video_devdata(file); 881ec6859b2STodor Tomov 882ec6859b2STodor Tomov vb2_fop_release(file); 883ec6859b2STodor Tomov 8848fd390b8SEzequiel Garcia v4l2_pipeline_pm_put(&vdev->entity); 885ec6859b2STodor Tomov 886ec6859b2STodor Tomov file->private_data = NULL; 887ec6859b2STodor Tomov 888ec6859b2STodor Tomov return 0; 889ec6859b2STodor Tomov } 890ec6859b2STodor Tomov 891ec6859b2STodor Tomov static const struct v4l2_file_operations msm_vid_fops = { 892ec6859b2STodor Tomov .owner = THIS_MODULE, 893ec6859b2STodor Tomov .unlocked_ioctl = video_ioctl2, 894ec6859b2STodor Tomov .open = video_open, 895ec6859b2STodor Tomov .release = video_release, 896ec6859b2STodor Tomov .poll = vb2_fop_poll, 897ec6859b2STodor Tomov .mmap = vb2_fop_mmap, 898ec6859b2STodor Tomov .read = vb2_fop_read, 899ec6859b2STodor Tomov }; 900ec6859b2STodor Tomov 901ec6859b2STodor Tomov /* ----------------------------------------------------------------------------- 902ec6859b2STodor Tomov * CAMSS video core 903ec6859b2STodor Tomov */ 904ec6859b2STodor Tomov 905ec6859b2STodor Tomov static void msm_video_release(struct video_device *vdev) 906ec6859b2STodor Tomov { 907ec6859b2STodor Tomov struct camss_video *video = video_get_drvdata(vdev); 908ec6859b2STodor Tomov 909ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 910ec6859b2STodor Tomov 911ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 912ec6859b2STodor Tomov mutex_destroy(&video->lock); 913ec6859b2STodor Tomov 914ec6859b2STodor Tomov if (atomic_dec_and_test(&video->camss->ref_count)) 915ec6859b2STodor Tomov camss_delete(video->camss); 916ec6859b2STodor Tomov } 917ec6859b2STodor Tomov 918ec6859b2STodor Tomov /* 919ec6859b2STodor Tomov * msm_video_init_format - Helper function to initialize format 920ec6859b2STodor Tomov * @video: struct camss_video 921ec6859b2STodor Tomov * 922ec6859b2STodor Tomov * Initialize pad format with default value. 923ec6859b2STodor Tomov * 924ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 925ec6859b2STodor Tomov */ 926ec6859b2STodor Tomov static int msm_video_init_format(struct camss_video *video) 927ec6859b2STodor Tomov { 928ec6859b2STodor Tomov int ret; 929ec6859b2STodor Tomov struct v4l2_format format = { 930ec6859b2STodor Tomov .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 931ec6859b2STodor Tomov .fmt.pix_mp = { 932ec6859b2STodor Tomov .width = 1920, 933ec6859b2STodor Tomov .height = 1080, 934ec6859b2STodor Tomov .pixelformat = video->formats[0].pixelformat, 935ec6859b2STodor Tomov }, 936ec6859b2STodor Tomov }; 937ec6859b2STodor Tomov 938ec6859b2STodor Tomov ret = __video_try_fmt(video, &format); 939ec6859b2STodor Tomov if (ret < 0) 940ec6859b2STodor Tomov return ret; 941ec6859b2STodor Tomov 942ec6859b2STodor Tomov video->active_fmt = format; 943ec6859b2STodor Tomov 944ec6859b2STodor Tomov return 0; 945ec6859b2STodor Tomov } 946ec6859b2STodor Tomov 947ec6859b2STodor Tomov /* 948ec6859b2STodor Tomov * msm_video_register - Register a video device node 949ec6859b2STodor Tomov * @video: struct camss_video 950ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 951ec6859b2STodor Tomov * @name: name to be used for the video device node 952ec6859b2STodor Tomov * 953ec6859b2STodor Tomov * Initialize and register a video device node to a V4L2 device. Also 954ec6859b2STodor Tomov * initialize the vb2 queue. 955ec6859b2STodor Tomov * 956ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 957ec6859b2STodor Tomov */ 958ec6859b2STodor Tomov 959ec6859b2STodor Tomov int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, 960ec6859b2STodor Tomov const char *name, int is_pix) 961ec6859b2STodor Tomov { 962ec6859b2STodor Tomov struct media_pad *pad = &video->pad; 963ec6859b2STodor Tomov struct video_device *vdev; 964ec6859b2STodor Tomov struct vb2_queue *q; 965ec6859b2STodor Tomov int ret; 966ec6859b2STodor Tomov 967ec6859b2STodor Tomov vdev = &video->vdev; 968ec6859b2STodor Tomov 969ec6859b2STodor Tomov mutex_init(&video->q_lock); 970ec6859b2STodor Tomov 971ec6859b2STodor Tomov q = &video->vb2_q; 972ec6859b2STodor Tomov q->drv_priv = video; 973ec6859b2STodor Tomov q->mem_ops = &vb2_dma_sg_memops; 974ec6859b2STodor Tomov q->ops = &msm_video_vb2_q_ops; 975ec6859b2STodor Tomov q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 976ec6859b2STodor Tomov q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; 977ec6859b2STodor Tomov q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 978ec6859b2STodor Tomov q->buf_struct_size = sizeof(struct camss_buffer); 979ec6859b2STodor Tomov q->dev = video->camss->dev; 980ec6859b2STodor Tomov q->lock = &video->q_lock; 981ec6859b2STodor Tomov ret = vb2_queue_init(q); 982ec6859b2STodor Tomov if (ret < 0) { 983ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret); 984ec6859b2STodor Tomov goto error_vb2_init; 985ec6859b2STodor Tomov } 986ec6859b2STodor Tomov 987ec6859b2STodor Tomov pad->flags = MEDIA_PAD_FL_SINK; 988ec6859b2STodor Tomov ret = media_entity_pads_init(&vdev->entity, 1, pad); 989ec6859b2STodor Tomov if (ret < 0) { 990ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", 991ec6859b2STodor Tomov ret); 992492abcd7SHans Verkuil goto error_vb2_init; 993ec6859b2STodor Tomov } 994ec6859b2STodor Tomov 995ec6859b2STodor Tomov mutex_init(&video->lock); 996ec6859b2STodor Tomov 997cba3819dSTodor Tomov if (video->camss->version == CAMSS_8x16) { 998ec6859b2STodor Tomov if (is_pix) { 999cba3819dSTodor Tomov video->formats = formats_pix_8x16; 1000cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x16); 1001cba3819dSTodor Tomov } else { 1002cba3819dSTodor Tomov video->formats = formats_rdi_8x16; 1003cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x16); 1004cba3819dSTodor Tomov } 10059e5d1581SAngeloGioacchino Del Regno } else if (video->camss->version == CAMSS_8x96 || 10069e5d1581SAngeloGioacchino Del Regno video->camss->version == CAMSS_660) { 1007cba3819dSTodor Tomov if (is_pix) { 1008cba3819dSTodor Tomov video->formats = formats_pix_8x96; 1009cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_pix_8x96); 1010cba3819dSTodor Tomov } else { 1011cba3819dSTodor Tomov video->formats = formats_rdi_8x96; 1012cba3819dSTodor Tomov video->nformats = ARRAY_SIZE(formats_rdi_8x96); 1013cba3819dSTodor Tomov } 1014*b4436a18SJonathan Marek } else if (video->camss->version == CAMSS_845 || 1015*b4436a18SJonathan Marek video->camss->version == CAMSS_8250) { 10167319cdf1SRobert Foss video->formats = formats_rdi_845; 10177319cdf1SRobert Foss video->nformats = ARRAY_SIZE(formats_rdi_845); 1018cba3819dSTodor Tomov } else { 10199c67ed2aSDan Carpenter ret = -EINVAL; 1020cba3819dSTodor Tomov goto error_video_register; 1021ec6859b2STodor Tomov } 1022ec6859b2STodor Tomov 1023ec6859b2STodor Tomov ret = msm_video_init_format(video); 1024ec6859b2STodor Tomov if (ret < 0) { 1025ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); 1026ec6859b2STodor Tomov goto error_video_register; 1027ec6859b2STodor Tomov } 1028ec6859b2STodor Tomov 1029ec6859b2STodor Tomov vdev->fops = &msm_vid_fops; 1030dfb5d328SAndrey Konovalov vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING 1031dfb5d328SAndrey Konovalov | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; 1032ec6859b2STodor Tomov vdev->ioctl_ops = &msm_vid_ioctl_ops; 1033ec6859b2STodor Tomov vdev->release = msm_video_release; 1034ec6859b2STodor Tomov vdev->v4l2_dev = v4l2_dev; 1035ec6859b2STodor Tomov vdev->vfl_dir = VFL_DIR_RX; 1036ec6859b2STodor Tomov vdev->queue = &video->vb2_q; 1037ec6859b2STodor Tomov vdev->lock = &video->lock; 1038c0decac1SMauro Carvalho Chehab strscpy(vdev->name, name, sizeof(vdev->name)); 1039ec6859b2STodor Tomov 104070cad449SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 1041ec6859b2STodor Tomov if (ret < 0) { 1042ec6859b2STodor Tomov dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", 1043ec6859b2STodor Tomov ret); 1044ec6859b2STodor Tomov goto error_video_register; 1045ec6859b2STodor Tomov } 1046ec6859b2STodor Tomov 1047ec6859b2STodor Tomov video_set_drvdata(vdev, video); 1048ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 1049ec6859b2STodor Tomov 1050ec6859b2STodor Tomov return 0; 1051ec6859b2STodor Tomov 1052ec6859b2STodor Tomov error_video_register: 1053ec6859b2STodor Tomov media_entity_cleanup(&vdev->entity); 1054ec6859b2STodor Tomov mutex_destroy(&video->lock); 1055ec6859b2STodor Tomov error_vb2_init: 1056ec6859b2STodor Tomov mutex_destroy(&video->q_lock); 1057ec6859b2STodor Tomov 1058ec6859b2STodor Tomov return ret; 1059ec6859b2STodor Tomov } 1060ec6859b2STodor Tomov 1061ec6859b2STodor Tomov void msm_video_unregister(struct camss_video *video) 1062ec6859b2STodor Tomov { 1063ec6859b2STodor Tomov atomic_inc(&video->camss->ref_count); 1064492abcd7SHans Verkuil vb2_video_unregister_device(&video->vdev); 1065ec6859b2STodor Tomov atomic_dec(&video->camss->ref_count); 1066ec6859b2STodor Tomov } 1067