xref: /linux/drivers/media/platform/qcom/camss/camss-vfe.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0
24c98a5f5STodor Tomov /*
34c98a5f5STodor Tomov  * camss-vfe.c
44c98a5f5STodor Tomov  *
5be744eecSTodor Tomov  * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
64c98a5f5STodor Tomov  *
74c98a5f5STodor Tomov  * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
8ec6859b2STodor Tomov  * Copyright (C) 2015-2018 Linaro Ltd.
94c98a5f5STodor Tomov  */
104c98a5f5STodor Tomov #include <linux/clk.h>
114c98a5f5STodor Tomov #include <linux/completion.h>
124c98a5f5STodor Tomov #include <linux/interrupt.h>
134c98a5f5STodor Tomov #include <linux/iommu.h>
144c98a5f5STodor Tomov #include <linux/mutex.h>
154c98a5f5STodor Tomov #include <linux/of.h>
164c98a5f5STodor Tomov #include <linux/platform_device.h>
1723aa4f0cSBryan O'Donoghue #include <linux/pm_domain.h>
1802afa816STodor Tomov #include <linux/pm_runtime.h>
194c98a5f5STodor Tomov #include <linux/spinlock_types.h>
204c98a5f5STodor Tomov #include <linux/spinlock.h>
214c98a5f5STodor Tomov #include <media/media-entity.h>
224c98a5f5STodor Tomov #include <media/v4l2-device.h>
234c98a5f5STodor Tomov #include <media/v4l2-subdev.h>
244c98a5f5STodor Tomov 
254c98a5f5STodor Tomov #include "camss-vfe.h"
264c98a5f5STodor Tomov #include "camss.h"
274c98a5f5STodor Tomov 
284c98a5f5STodor Tomov #define MSM_VFE_NAME "msm_vfe"
294c98a5f5STodor Tomov 
304c98a5f5STodor Tomov /* VFE reset timeout */
314c98a5f5STodor Tomov #define VFE_RESET_TIMEOUT_MS 50
329b5833f7STodor Tomov 
33810b6598STodor Tomov #define SCALER_RATIO_MAX 16
34810b6598STodor Tomov 
3510693fedSDepeng Shao #define VFE_HW_VERSION		0x0
3610693fedSDepeng Shao #define		HW_VERSION_STEPPING	0
3710693fedSDepeng Shao #define		HW_VERSION_REVISION	16
3810693fedSDepeng Shao #define		HW_VERSION_GENERATION	28
3910693fedSDepeng Shao 
406c46cb0dSRadoslav Tsvetkov static const struct camss_format_info formats_rdi_8x16[] = {
416c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
426c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
436c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
446c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
456c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
466c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
476c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
486c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
496c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
506c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
516c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
526c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
536c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
546c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
556c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
566c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
576c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
586c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
596c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
606c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
616c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
626c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
636c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
646c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
656c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
666c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
676c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
686c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
696c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
706c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
716c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
726c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
736c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
746c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
75cba3819dSTodor Tomov };
76cba3819dSTodor Tomov 
776c46cb0dSRadoslav Tsvetkov static const struct camss_format_info formats_rdi_8x96[] = {
786c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
796c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
806c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
816c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
826c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
836c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
846c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
856c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
866c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
876c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
886c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
896c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
906c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
916c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
926c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
936c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
946c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
956c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
966c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
976c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
986c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
996c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1006c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
1016c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1026c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1,
1036c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1046c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
1056c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1066c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
1076c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1086c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
1096c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1106c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
1116c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1126c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1,
1136c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1146c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1,
1156c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1166c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1,
1176c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1186c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1,
1196c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1206c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
1216c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1226c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1,
1236c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
124cba3819dSTodor Tomov };
125cba3819dSTodor Tomov 
1266c46cb0dSRadoslav Tsvetkov static const struct camss_format_info formats_rdi_845[] = {
1276c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
1286c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1296c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
1306c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1316c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
1326c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1336c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
1346c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1356c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
1366c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
1376c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
1386c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
1396c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
1406c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
1416c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
1426c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
1436c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
1446c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1456c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
1466c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1476c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
1486c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1496c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
1506c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1516c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1,
1526c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
1536c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
1546c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1556c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
1566c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1576c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
1586c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1596c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
1606c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
1616c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1,
1626c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1636c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1,
1646c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1656c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1,
1666c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1676c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1,
1686c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
1696c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1,
1706c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
1716c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
1726c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
1736c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1,
1746c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
175cba3819dSTodor Tomov };
176cba3819dSTodor Tomov 
1776c46cb0dSRadoslav Tsvetkov static const struct camss_format_info formats_pix_8x16[] = {
1786c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
1796c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1806c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
1816c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1826c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
1836c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1846c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
1856c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1866c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
1876c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1886c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
1896c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1906c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
1916c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1926c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
1936c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
1946c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
1956c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
1966c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
1976c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
1986c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
1996c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2006c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
2016c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2026c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2036c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2046c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2056c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2066c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2076c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2086c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2096c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
210cba3819dSTodor Tomov };
211cba3819dSTodor Tomov 
2126c46cb0dSRadoslav Tsvetkov static const struct camss_format_info formats_pix_8x96[] = {
2136c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
2146c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2156c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
2166c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2176c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
2186c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2196c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
2206c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2216c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
2226c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2236c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
2246c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2256c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
2266c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2276c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
2286c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
2296c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
2306c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2316c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
2326c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2336c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
2346c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2356c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
2366c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2376c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2386c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2396c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2406c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2416c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2426c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2436c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
2446c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
2456c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
2466c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
2476c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
2486c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
2496c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
2506c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
2516c46cb0dSRadoslav Tsvetkov 	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
2526c46cb0dSRadoslav Tsvetkov 	  PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
2534c98a5f5STodor Tomov };
2544c98a5f5STodor Tomov 
2556c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_rdi_8x16 = {
2566c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_rdi_8x16),
2576c46cb0dSRadoslav Tsvetkov 	.formats = formats_rdi_8x16
2586c46cb0dSRadoslav Tsvetkov };
2596c46cb0dSRadoslav Tsvetkov 
2606c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_pix_8x16 = {
2616c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_pix_8x16),
2626c46cb0dSRadoslav Tsvetkov 	.formats = formats_pix_8x16
2636c46cb0dSRadoslav Tsvetkov };
2646c46cb0dSRadoslav Tsvetkov 
2656c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_rdi_8x96 = {
2666c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_rdi_8x96),
2676c46cb0dSRadoslav Tsvetkov 	.formats = formats_rdi_8x96
2686c46cb0dSRadoslav Tsvetkov };
2696c46cb0dSRadoslav Tsvetkov 
2706c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_pix_8x96 = {
2716c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_pix_8x96),
2726c46cb0dSRadoslav Tsvetkov 	.formats = formats_pix_8x96
2736c46cb0dSRadoslav Tsvetkov };
2746c46cb0dSRadoslav Tsvetkov 
2756c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_rdi_845 = {
2766c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_rdi_845),
2776c46cb0dSRadoslav Tsvetkov 	.formats = formats_rdi_845
2786c46cb0dSRadoslav Tsvetkov };
2796c46cb0dSRadoslav Tsvetkov 
2806c46cb0dSRadoslav Tsvetkov /* TODO: Replace with pix formats */
2816c46cb0dSRadoslav Tsvetkov const struct camss_formats vfe_formats_pix_845 = {
2826c46cb0dSRadoslav Tsvetkov 	.nformats = ARRAY_SIZE(formats_rdi_845),
2836c46cb0dSRadoslav Tsvetkov 	.formats = formats_rdi_845
2847319cdf1SRobert Foss };
2857319cdf1SRobert Foss 
vfe_src_pad_code(struct vfe_line * line,u32 sink_code,unsigned int index,u32 src_req_code)28607eeb342STodor Tomov static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
28707eeb342STodor Tomov 			    unsigned int index, u32 src_req_code)
28807eeb342STodor Tomov {
28907eeb342STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
29007eeb342STodor Tomov 
2912de3a654SBryan O'Donoghue 	switch (vfe->camss->res->version) {
2922de3a654SBryan O'Donoghue 	case CAMSS_8x16:
2937ee35e07SVladimir Lypak 	case CAMSS_8x53:
29407eeb342STodor Tomov 		switch (sink_code) {
29589936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YUYV8_1X16:
29607eeb342STodor Tomov 		{
29707eeb342STodor Tomov 			u32 src_code[] = {
29889936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
29907eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
30007eeb342STodor Tomov 			};
30107eeb342STodor Tomov 
302ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
30307eeb342STodor Tomov 						      index, src_req_code);
30407eeb342STodor Tomov 		}
30589936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YVYU8_1X16:
30607eeb342STodor Tomov 		{
30707eeb342STodor Tomov 			u32 src_code[] = {
30889936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
30907eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
31007eeb342STodor Tomov 			};
31107eeb342STodor Tomov 
312ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
31307eeb342STodor Tomov 						      index, src_req_code);
31407eeb342STodor Tomov 		}
31589936bfbSMartin Dørum 		case MEDIA_BUS_FMT_UYVY8_1X16:
31607eeb342STodor Tomov 		{
31707eeb342STodor Tomov 			u32 src_code[] = {
31889936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
31907eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
32007eeb342STodor Tomov 			};
32107eeb342STodor Tomov 
322ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
32307eeb342STodor Tomov 						      index, src_req_code);
32407eeb342STodor Tomov 		}
32589936bfbSMartin Dørum 		case MEDIA_BUS_FMT_VYUY8_1X16:
32607eeb342STodor Tomov 		{
32707eeb342STodor Tomov 			u32 src_code[] = {
32889936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
32907eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
33007eeb342STodor Tomov 			};
33107eeb342STodor Tomov 
332ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
33307eeb342STodor Tomov 						      index, src_req_code);
33407eeb342STodor Tomov 		}
33507eeb342STodor Tomov 		default:
33607eeb342STodor Tomov 			if (index > 0)
33707eeb342STodor Tomov 				return 0;
33807eeb342STodor Tomov 
33907eeb342STodor Tomov 			return sink_code;
34007eeb342STodor Tomov 		}
3412de3a654SBryan O'Donoghue 		break;
3422de3a654SBryan O'Donoghue 	case CAMSS_660:
343a7f5b36bSSuresh Vankadara 	case CAMSS_7280:
3443522673bSVikram Sharma 	case CAMSS_8x96:
3452de3a654SBryan O'Donoghue 	case CAMSS_8250:
346f2a7ffabSBryan O'Donoghue 	case CAMSS_8280XP:
3473522673bSVikram Sharma 	case CAMSS_845:
34839e3f5bcSDepeng Shao 	case CAMSS_8550:
349*1830cf0fSBryan O'Donoghue 	case CAMSS_X1E80100:
35007eeb342STodor Tomov 		switch (sink_code) {
35189936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YUYV8_1X16:
35207eeb342STodor Tomov 		{
35307eeb342STodor Tomov 			u32 src_code[] = {
35489936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
35589936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
35689936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
35789936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
35807eeb342STodor Tomov 				MEDIA_BUS_FMT_YUYV8_1_5X8,
35907eeb342STodor Tomov 			};
36007eeb342STodor Tomov 
361ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
36207eeb342STodor Tomov 						      index, src_req_code);
36307eeb342STodor Tomov 		}
36489936bfbSMartin Dørum 		case MEDIA_BUS_FMT_YVYU8_1X16:
36507eeb342STodor Tomov 		{
36607eeb342STodor Tomov 			u32 src_code[] = {
36789936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
36889936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
36989936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
37089936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
37107eeb342STodor Tomov 				MEDIA_BUS_FMT_YVYU8_1_5X8,
37207eeb342STodor Tomov 			};
37307eeb342STodor Tomov 
374ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
37507eeb342STodor Tomov 						      index, src_req_code);
37607eeb342STodor Tomov 		}
37789936bfbSMartin Dørum 		case MEDIA_BUS_FMT_UYVY8_1X16:
37807eeb342STodor Tomov 		{
37907eeb342STodor Tomov 			u32 src_code[] = {
38089936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
38189936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
38289936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
38389936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
38407eeb342STodor Tomov 				MEDIA_BUS_FMT_UYVY8_1_5X8,
38507eeb342STodor Tomov 			};
38607eeb342STodor Tomov 
387ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
38807eeb342STodor Tomov 						      index, src_req_code);
38907eeb342STodor Tomov 		}
39089936bfbSMartin Dørum 		case MEDIA_BUS_FMT_VYUY8_1X16:
39107eeb342STodor Tomov 		{
39207eeb342STodor Tomov 			u32 src_code[] = {
39389936bfbSMartin Dørum 				MEDIA_BUS_FMT_VYUY8_1X16,
39489936bfbSMartin Dørum 				MEDIA_BUS_FMT_YUYV8_1X16,
39589936bfbSMartin Dørum 				MEDIA_BUS_FMT_YVYU8_1X16,
39689936bfbSMartin Dørum 				MEDIA_BUS_FMT_UYVY8_1X16,
39707eeb342STodor Tomov 				MEDIA_BUS_FMT_VYUY8_1_5X8,
39807eeb342STodor Tomov 			};
39907eeb342STodor Tomov 
400ad458cb9SRadoslav Tsvetkov 			return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
40107eeb342STodor Tomov 						      index, src_req_code);
40207eeb342STodor Tomov 		}
40307eeb342STodor Tomov 		default:
40407eeb342STodor Tomov 			if (index > 0)
40507eeb342STodor Tomov 				return 0;
40607eeb342STodor Tomov 
40707eeb342STodor Tomov 			return sink_code;
40807eeb342STodor Tomov 		}
4092de3a654SBryan O'Donoghue 		break;
4102f4204bbSDepeng Shao 	default:
4112f4204bbSDepeng Shao 		WARN(1, "Unsupported HW version: %x\n",
4122f4204bbSDepeng Shao 		     vfe->camss->res->version);
4132f4204bbSDepeng Shao 		break;
4142de3a654SBryan O'Donoghue 	}
41507eeb342STodor Tomov 	return 0;
41607eeb342STodor Tomov }
41707eeb342STodor Tomov 
41810693fedSDepeng Shao /*
41910693fedSDepeng Shao  * vfe_hw_version - Process write master done interrupt
42010693fedSDepeng Shao  * @vfe: VFE Device
42110693fedSDepeng Shao  *
42210693fedSDepeng Shao  * Return vfe hw version
42310693fedSDepeng Shao  */
vfe_hw_version(struct vfe_device * vfe)42410693fedSDepeng Shao u32 vfe_hw_version(struct vfe_device *vfe)
42510693fedSDepeng Shao {
42610693fedSDepeng Shao 	u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
42710693fedSDepeng Shao 
42810693fedSDepeng Shao 	u32 gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
42910693fedSDepeng Shao 	u32 rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
43010693fedSDepeng Shao 	u32 step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
43110693fedSDepeng Shao 
432b6fafb39SJohan Hovold 	dev_dbg(vfe->camss->dev, "VFE:%d HW Version = %u.%u.%u\n",
43310693fedSDepeng Shao 		vfe->id, gen, rev, step);
43410693fedSDepeng Shao 
43510693fedSDepeng Shao 	return hw_version;
43610693fedSDepeng Shao }
43710693fedSDepeng Shao 
43810693fedSDepeng Shao /*
43910693fedSDepeng Shao  * vfe_buf_done - Process write master done interrupt
44010693fedSDepeng Shao  * @vfe: VFE Device
44110693fedSDepeng Shao  * @wm: Write master id
44210693fedSDepeng Shao  */
vfe_buf_done(struct vfe_device * vfe,int wm)44310693fedSDepeng Shao void vfe_buf_done(struct vfe_device *vfe, int wm)
44410693fedSDepeng Shao {
44510693fedSDepeng Shao 	struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
44610693fedSDepeng Shao 	const struct vfe_hw_ops *ops = vfe->res->hw_ops;
44710693fedSDepeng Shao 	struct camss_buffer *ready_buf;
44810693fedSDepeng Shao 	struct vfe_output *output;
44910693fedSDepeng Shao 	unsigned long flags;
45010693fedSDepeng Shao 	u32 index;
45110693fedSDepeng Shao 	u64 ts = ktime_get_ns();
45210693fedSDepeng Shao 
45310693fedSDepeng Shao 	spin_lock_irqsave(&vfe->output_lock, flags);
45410693fedSDepeng Shao 
45510693fedSDepeng Shao 	if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
45610693fedSDepeng Shao 		dev_err_ratelimited(vfe->camss->dev,
45710693fedSDepeng Shao 				    "Received wm done for unmapped index\n");
45810693fedSDepeng Shao 		goto out_unlock;
45910693fedSDepeng Shao 	}
46010693fedSDepeng Shao 	output = &vfe->line[vfe->wm_output_map[wm]].output;
46110693fedSDepeng Shao 
46210693fedSDepeng Shao 	ready_buf = output->buf[0];
46310693fedSDepeng Shao 	if (!ready_buf) {
46410693fedSDepeng Shao 		dev_err_ratelimited(vfe->camss->dev,
46510693fedSDepeng Shao 				    "Missing ready buf %d!\n", output->state);
46610693fedSDepeng Shao 		goto out_unlock;
46710693fedSDepeng Shao 	}
46810693fedSDepeng Shao 
46910693fedSDepeng Shao 	ready_buf->vb.vb2_buf.timestamp = ts;
47010693fedSDepeng Shao 	ready_buf->vb.sequence = output->sequence++;
47110693fedSDepeng Shao 
47210693fedSDepeng Shao 	index = 0;
47310693fedSDepeng Shao 	output->buf[0] = output->buf[1];
47410693fedSDepeng Shao 	if (output->buf[0])
47510693fedSDepeng Shao 		index = 1;
47610693fedSDepeng Shao 
47710693fedSDepeng Shao 	output->buf[index] = vfe_buf_get_pending(output);
47810693fedSDepeng Shao 
47910693fedSDepeng Shao 	if (output->buf[index]) {
48010693fedSDepeng Shao 		ops->vfe_wm_update(vfe, output->wm_idx[0],
48110693fedSDepeng Shao 				   output->buf[index]->addr[0],
48210693fedSDepeng Shao 				   line);
48310693fedSDepeng Shao 		ops->reg_update(vfe, line->id);
48410693fedSDepeng Shao 	} else {
48510693fedSDepeng Shao 		output->gen2.active_num--;
48610693fedSDepeng Shao 	}
48710693fedSDepeng Shao 
48810693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
48910693fedSDepeng Shao 
49010693fedSDepeng Shao 	vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
49110693fedSDepeng Shao 
49210693fedSDepeng Shao 	return;
49310693fedSDepeng Shao 
49410693fedSDepeng Shao out_unlock:
49510693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
49610693fedSDepeng Shao }
49710693fedSDepeng Shao 
vfe_enable_output_v2(struct vfe_line * line)49810693fedSDepeng Shao int vfe_enable_output_v2(struct vfe_line *line)
49910693fedSDepeng Shao {
50010693fedSDepeng Shao 	struct vfe_device *vfe = to_vfe(line);
50110693fedSDepeng Shao 	struct vfe_output *output = &line->output;
50210693fedSDepeng Shao 	const struct vfe_hw_ops *ops = vfe->res->hw_ops;
503e7bad98cSSakari Ailus 	struct media_pad *sensor_pad;
50410693fedSDepeng Shao 	unsigned long flags;
50510693fedSDepeng Shao 	unsigned int frame_skip = 0;
50610693fedSDepeng Shao 	unsigned int i;
50710693fedSDepeng Shao 
508e7bad98cSSakari Ailus 	sensor_pad = camss_find_sensor_pad(&line->subdev.entity);
509e7bad98cSSakari Ailus 	if (sensor_pad) {
510e7bad98cSSakari Ailus 		struct v4l2_subdev *subdev =
511e7bad98cSSakari Ailus 			media_entity_to_v4l2_subdev(sensor_pad->entity);
51210693fedSDepeng Shao 
51310693fedSDepeng Shao 		v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
51410693fedSDepeng Shao 		/* Max frame skip is 29 frames */
51510693fedSDepeng Shao 		if (frame_skip > VFE_FRAME_DROP_VAL - 1)
51610693fedSDepeng Shao 			frame_skip = VFE_FRAME_DROP_VAL - 1;
51710693fedSDepeng Shao 	}
51810693fedSDepeng Shao 
51910693fedSDepeng Shao 	spin_lock_irqsave(&vfe->output_lock, flags);
52010693fedSDepeng Shao 
52110693fedSDepeng Shao 	ops->reg_update_clear(vfe, line->id);
52210693fedSDepeng Shao 
52310693fedSDepeng Shao 	if (output->state > VFE_OUTPUT_RESERVED) {
52410693fedSDepeng Shao 		dev_err(vfe->camss->dev,
52510693fedSDepeng Shao 			"Output is not in reserved state %d\n",
52610693fedSDepeng Shao 			output->state);
52710693fedSDepeng Shao 		spin_unlock_irqrestore(&vfe->output_lock, flags);
52810693fedSDepeng Shao 		return -EINVAL;
52910693fedSDepeng Shao 	}
53010693fedSDepeng Shao 
53110693fedSDepeng Shao 	WARN_ON(output->gen2.active_num);
53210693fedSDepeng Shao 
53310693fedSDepeng Shao 	output->state = VFE_OUTPUT_ON;
53410693fedSDepeng Shao 
53510693fedSDepeng Shao 	output->sequence = 0;
53610693fedSDepeng Shao 	output->wait_reg_update = 0;
53710693fedSDepeng Shao 	reinit_completion(&output->reg_update);
53810693fedSDepeng Shao 
53910693fedSDepeng Shao 	ops->vfe_wm_start(vfe, output->wm_idx[0], line);
54010693fedSDepeng Shao 
54110693fedSDepeng Shao 	for (i = 0; i < 2; i++) {
54210693fedSDepeng Shao 		output->buf[i] = vfe_buf_get_pending(output);
54310693fedSDepeng Shao 		if (!output->buf[i])
54410693fedSDepeng Shao 			break;
54510693fedSDepeng Shao 		output->gen2.active_num++;
54610693fedSDepeng Shao 		ops->vfe_wm_update(vfe, output->wm_idx[0],
54710693fedSDepeng Shao 				   output->buf[i]->addr[0], line);
54810693fedSDepeng Shao 		ops->reg_update(vfe, line->id);
54910693fedSDepeng Shao 	}
55010693fedSDepeng Shao 
55110693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
55210693fedSDepeng Shao 
55310693fedSDepeng Shao 	return 0;
55410693fedSDepeng Shao }
55510693fedSDepeng Shao 
55610693fedSDepeng Shao /*
55710693fedSDepeng Shao  * vfe_queue_buffer_v2 - Add empty buffer
55810693fedSDepeng Shao  * @vid: Video device structure
55910693fedSDepeng Shao  * @buf: Buffer to be enqueued
56010693fedSDepeng Shao  *
56110693fedSDepeng Shao  * Add an empty buffer - depending on the current number of buffers it will be
56210693fedSDepeng Shao  * put in pending buffer queue or directly given to the hardware to be filled.
56310693fedSDepeng Shao  *
56410693fedSDepeng Shao  * Return 0 on success or a negative error code otherwise
56510693fedSDepeng Shao  */
vfe_queue_buffer_v2(struct camss_video * vid,struct camss_buffer * buf)56610693fedSDepeng Shao int vfe_queue_buffer_v2(struct camss_video *vid,
56710693fedSDepeng Shao 			struct camss_buffer *buf)
56810693fedSDepeng Shao {
56910693fedSDepeng Shao 	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
57010693fedSDepeng Shao 	struct vfe_device *vfe = to_vfe(line);
57110693fedSDepeng Shao 	const struct vfe_hw_ops *ops = vfe->res->hw_ops;
57210693fedSDepeng Shao 	struct vfe_output *output;
57310693fedSDepeng Shao 	unsigned long flags;
57410693fedSDepeng Shao 
57510693fedSDepeng Shao 	output = &line->output;
57610693fedSDepeng Shao 
57710693fedSDepeng Shao 	spin_lock_irqsave(&vfe->output_lock, flags);
57810693fedSDepeng Shao 
57910693fedSDepeng Shao 	if (output->state == VFE_OUTPUT_ON &&
58010693fedSDepeng Shao 	    output->gen2.active_num < 2) {
58110693fedSDepeng Shao 		output->buf[output->gen2.active_num++] = buf;
58210693fedSDepeng Shao 		ops->vfe_wm_update(vfe, output->wm_idx[0],
58310693fedSDepeng Shao 				   buf->addr[0], line);
58410693fedSDepeng Shao 		ops->reg_update(vfe, line->id);
58510693fedSDepeng Shao 	} else {
58610693fedSDepeng Shao 		vfe_buf_add_pending(output, buf);
58710693fedSDepeng Shao 	}
58810693fedSDepeng Shao 
58910693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
59010693fedSDepeng Shao 
59110693fedSDepeng Shao 	return 0;
59210693fedSDepeng Shao }
59310693fedSDepeng Shao 
59410693fedSDepeng Shao /*
59510693fedSDepeng Shao  * vfe_enable_v2 - Enable streaming on VFE line
59610693fedSDepeng Shao  * @line: VFE line
59710693fedSDepeng Shao  *
59810693fedSDepeng Shao  * Return 0 on success or a negative error code otherwise
59910693fedSDepeng Shao  */
vfe_enable_v2(struct vfe_line * line)60010693fedSDepeng Shao int vfe_enable_v2(struct vfe_line *line)
60110693fedSDepeng Shao {
60210693fedSDepeng Shao 	struct vfe_device *vfe = to_vfe(line);
60310693fedSDepeng Shao 	const struct vfe_hw_ops *ops = vfe->res->hw_ops;
60410693fedSDepeng Shao 	int ret;
60510693fedSDepeng Shao 
60610693fedSDepeng Shao 	mutex_lock(&vfe->stream_lock);
60710693fedSDepeng Shao 
60810693fedSDepeng Shao 	if (vfe->res->hw_ops->enable_irq)
60910693fedSDepeng Shao 		ops->enable_irq(vfe);
61010693fedSDepeng Shao 
61110693fedSDepeng Shao 	vfe->stream_count++;
61210693fedSDepeng Shao 
61310693fedSDepeng Shao 	mutex_unlock(&vfe->stream_lock);
61410693fedSDepeng Shao 
61510693fedSDepeng Shao 	ret = vfe_get_output_v2(line);
61610693fedSDepeng Shao 	if (ret < 0)
61710693fedSDepeng Shao 		goto error_get_output;
61810693fedSDepeng Shao 
61910693fedSDepeng Shao 	ret = vfe_enable_output_v2(line);
62010693fedSDepeng Shao 	if (ret < 0)
62110693fedSDepeng Shao 		goto error_enable_output;
62210693fedSDepeng Shao 
62310693fedSDepeng Shao 	vfe->was_streaming = 1;
62410693fedSDepeng Shao 
62510693fedSDepeng Shao 	return 0;
62610693fedSDepeng Shao 
62710693fedSDepeng Shao error_enable_output:
62810693fedSDepeng Shao 	vfe_put_output(line);
62910693fedSDepeng Shao 
63010693fedSDepeng Shao error_get_output:
63110693fedSDepeng Shao 	mutex_lock(&vfe->stream_lock);
63210693fedSDepeng Shao 
63310693fedSDepeng Shao 	vfe->stream_count--;
63410693fedSDepeng Shao 
63510693fedSDepeng Shao 	mutex_unlock(&vfe->stream_lock);
63610693fedSDepeng Shao 
63710693fedSDepeng Shao 	return ret;
63810693fedSDepeng Shao }
63910693fedSDepeng Shao 
64010693fedSDepeng Shao /*
64110693fedSDepeng Shao  * vfe_get_output_v2 - Get vfe output port for corresponding VFE line
64210693fedSDepeng Shao  * @line: VFE line
64310693fedSDepeng Shao  *
64410693fedSDepeng Shao  * Return 0 on success or a negative error code otherwise
64510693fedSDepeng Shao  */
vfe_get_output_v2(struct vfe_line * line)64610693fedSDepeng Shao int vfe_get_output_v2(struct vfe_line *line)
64710693fedSDepeng Shao {
64810693fedSDepeng Shao 	struct vfe_device *vfe = to_vfe(line);
64910693fedSDepeng Shao 	struct vfe_output *output;
65010693fedSDepeng Shao 	unsigned long flags;
65110693fedSDepeng Shao 
65210693fedSDepeng Shao 	spin_lock_irqsave(&vfe->output_lock, flags);
65310693fedSDepeng Shao 
65410693fedSDepeng Shao 	output = &line->output;
65510693fedSDepeng Shao 	if (output->state > VFE_OUTPUT_RESERVED) {
65610693fedSDepeng Shao 		dev_err(vfe->camss->dev, "Output is running\n");
65710693fedSDepeng Shao 		goto error;
65810693fedSDepeng Shao 	}
65910693fedSDepeng Shao 
66010693fedSDepeng Shao 	output->wm_num = 1;
66110693fedSDepeng Shao 
66210693fedSDepeng Shao 	/* Correspondence between VFE line number and WM number.
66310693fedSDepeng Shao 	 * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
66410693fedSDepeng Shao 	 * Note this 1:1 mapping will not work for PIX streams.
66510693fedSDepeng Shao 	 */
66610693fedSDepeng Shao 	output->wm_idx[0] = line->id;
66710693fedSDepeng Shao 	vfe->wm_output_map[line->id] = line->id;
66810693fedSDepeng Shao 
66910693fedSDepeng Shao 	output->drop_update_idx = 0;
67010693fedSDepeng Shao 
67110693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
67210693fedSDepeng Shao 
67310693fedSDepeng Shao 	return 0;
67410693fedSDepeng Shao 
67510693fedSDepeng Shao error:
67610693fedSDepeng Shao 	spin_unlock_irqrestore(&vfe->output_lock, flags);
67710693fedSDepeng Shao 	output->state = VFE_OUTPUT_OFF;
67810693fedSDepeng Shao 
67910693fedSDepeng Shao 	return -EINVAL;
68010693fedSDepeng Shao }
68110693fedSDepeng Shao 
vfe_reset(struct vfe_device * vfe)6827319cdf1SRobert Foss int vfe_reset(struct vfe_device *vfe)
6834c98a5f5STodor Tomov {
6844c98a5f5STodor Tomov 	unsigned long time;
6854c98a5f5STodor Tomov 
6864c98a5f5STodor Tomov 	reinit_completion(&vfe->reset_complete);
6874c98a5f5STodor Tomov 
688ae44829aSRadoslav Tsvetkov 	vfe->res->hw_ops->global_reset(vfe);
6894c98a5f5STodor Tomov 
6904c98a5f5STodor Tomov 	time = wait_for_completion_timeout(&vfe->reset_complete,
6914c98a5f5STodor Tomov 		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
6924c98a5f5STodor Tomov 	if (!time) {
6939c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "VFE reset timeout\n");
6944c98a5f5STodor Tomov 		return -EIO;
6954c98a5f5STodor Tomov 	}
6964c98a5f5STodor Tomov 
6974c98a5f5STodor Tomov 	return 0;
6984c98a5f5STodor Tomov }
6994c98a5f5STodor Tomov 
vfe_init_outputs(struct vfe_device * vfe)7004c98a5f5STodor Tomov static void vfe_init_outputs(struct vfe_device *vfe)
7014c98a5f5STodor Tomov {
7024c98a5f5STodor Tomov 	int i;
7034c98a5f5STodor Tomov 
704ae44829aSRadoslav Tsvetkov 	for (i = 0; i < vfe->res->line_num; i++) {
7054c98a5f5STodor Tomov 		struct vfe_output *output = &vfe->line[i].output;
7064c98a5f5STodor Tomov 
7074c98a5f5STodor Tomov 		output->state = VFE_OUTPUT_OFF;
7084c98a5f5STodor Tomov 		output->buf[0] = NULL;
7094c98a5f5STodor Tomov 		output->buf[1] = NULL;
7104c98a5f5STodor Tomov 		INIT_LIST_HEAD(&output->pending_bufs);
7114c98a5f5STodor Tomov 	}
7124c98a5f5STodor Tomov }
7134c98a5f5STodor Tomov 
vfe_reset_output_maps(struct vfe_device * vfe)7144c98a5f5STodor Tomov static void vfe_reset_output_maps(struct vfe_device *vfe)
7154c98a5f5STodor Tomov {
7164c98a5f5STodor Tomov 	int i;
7174c98a5f5STodor Tomov 
7184c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
7194c98a5f5STodor Tomov 		vfe->wm_output_map[i] = VFE_LINE_NONE;
7204c98a5f5STodor Tomov }
7214c98a5f5STodor Tomov 
vfe_reserve_wm(struct vfe_device * vfe,enum vfe_line_id line_id)722633b388fSRobert Foss int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
7234c98a5f5STodor Tomov {
7244c98a5f5STodor Tomov 	int ret = -EBUSY;
7254c98a5f5STodor Tomov 	int i;
7264c98a5f5STodor Tomov 
7274c98a5f5STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
7284c98a5f5STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
7294c98a5f5STodor Tomov 			vfe->wm_output_map[i] = line_id;
7304c98a5f5STodor Tomov 			ret = i;
7314c98a5f5STodor Tomov 			break;
7324c98a5f5STodor Tomov 		}
7334c98a5f5STodor Tomov 	}
7344c98a5f5STodor Tomov 
7354c98a5f5STodor Tomov 	return ret;
7364c98a5f5STodor Tomov }
7374c98a5f5STodor Tomov 
vfe_release_wm(struct vfe_device * vfe,u8 wm)738633b388fSRobert Foss int vfe_release_wm(struct vfe_device *vfe, u8 wm)
7394c98a5f5STodor Tomov {
7406e893ca2SMauro Carvalho Chehab 	if (wm >= ARRAY_SIZE(vfe->wm_output_map))
7414c98a5f5STodor Tomov 		return -EINVAL;
7424c98a5f5STodor Tomov 
7434c98a5f5STodor Tomov 	vfe->wm_output_map[wm] = VFE_LINE_NONE;
7444c98a5f5STodor Tomov 
7454c98a5f5STodor Tomov 	return 0;
7464c98a5f5STodor Tomov }
7474c98a5f5STodor Tomov 
vfe_buf_get_pending(struct vfe_output * output)748633b388fSRobert Foss struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
7494c98a5f5STodor Tomov {
7504c98a5f5STodor Tomov 	struct camss_buffer *buffer = NULL;
7514c98a5f5STodor Tomov 
7524c98a5f5STodor Tomov 	if (!list_empty(&output->pending_bufs)) {
7534c98a5f5STodor Tomov 		buffer = list_first_entry(&output->pending_bufs,
7544c98a5f5STodor Tomov 					  struct camss_buffer,
7554c98a5f5STodor Tomov 					  queue);
7564c98a5f5STodor Tomov 		list_del(&buffer->queue);
7574c98a5f5STodor Tomov 	}
7584c98a5f5STodor Tomov 
7594c98a5f5STodor Tomov 	return buffer;
7604c98a5f5STodor Tomov }
7614c98a5f5STodor Tomov 
vfe_buf_add_pending(struct vfe_output * output,struct camss_buffer * buffer)762633b388fSRobert Foss void vfe_buf_add_pending(struct vfe_output *output,
7634c98a5f5STodor Tomov 			 struct camss_buffer *buffer)
7644c98a5f5STodor Tomov {
7654c98a5f5STodor Tomov 	INIT_LIST_HEAD(&buffer->queue);
7664c98a5f5STodor Tomov 	list_add_tail(&buffer->queue, &output->pending_bufs);
7674c98a5f5STodor Tomov }
7684c98a5f5STodor Tomov 
7694c98a5f5STodor Tomov /*
7704c98a5f5STodor Tomov  * vfe_buf_flush_pending - Flush all pending buffers.
7714c98a5f5STodor Tomov  * @output: VFE output
7724c98a5f5STodor Tomov  * @state: vb2 buffer state
7734c98a5f5STodor Tomov  */
vfe_buf_flush_pending(struct vfe_output * output,enum vb2_buffer_state state)7744c98a5f5STodor Tomov static void vfe_buf_flush_pending(struct vfe_output *output,
7754c98a5f5STodor Tomov 				  enum vb2_buffer_state state)
7764c98a5f5STodor Tomov {
7774c98a5f5STodor Tomov 	struct camss_buffer *buf;
7784c98a5f5STodor Tomov 	struct camss_buffer *t;
7794c98a5f5STodor Tomov 
7804c98a5f5STodor Tomov 	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
7814c98a5f5STodor Tomov 		vb2_buffer_done(&buf->vb.vb2_buf, state);
7824c98a5f5STodor Tomov 		list_del(&buf->queue);
7834c98a5f5STodor Tomov 	}
7844c98a5f5STodor Tomov }
7854c98a5f5STodor Tomov 
vfe_put_output(struct vfe_line * line)786633b388fSRobert Foss int vfe_put_output(struct vfe_line *line)
7874c98a5f5STodor Tomov {
7884c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
7894c98a5f5STodor Tomov 	struct vfe_output *output = &line->output;
7904c98a5f5STodor Tomov 	unsigned long flags;
7919b5833f7STodor Tomov 	unsigned int i;
7924c98a5f5STodor Tomov 
7934c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
7944c98a5f5STodor Tomov 
7959b5833f7STodor Tomov 	for (i = 0; i < output->wm_num; i++)
7969b5833f7STodor Tomov 		vfe_release_wm(vfe, output->wm_idx[i]);
7974c98a5f5STodor Tomov 
7984c98a5f5STodor Tomov 	output->state = VFE_OUTPUT_OFF;
7994c98a5f5STodor Tomov 
8004c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
8019b5833f7STodor Tomov 	return 0;
8024c98a5f5STodor Tomov }
8034c98a5f5STodor Tomov 
vfe_disable_output(struct vfe_line * line)80490cc4555SBryan O'Donoghue static int vfe_disable_output(struct vfe_line *line)
80590cc4555SBryan O'Donoghue {
80690cc4555SBryan O'Donoghue 	struct vfe_device *vfe = to_vfe(line);
80790cc4555SBryan O'Donoghue 	struct vfe_output *output = &line->output;
80890cc4555SBryan O'Donoghue 	unsigned long flags;
80990cc4555SBryan O'Donoghue 	unsigned int i;
81090cc4555SBryan O'Donoghue 
81190cc4555SBryan O'Donoghue 	spin_lock_irqsave(&vfe->output_lock, flags);
81290cc4555SBryan O'Donoghue 	for (i = 0; i < output->wm_num; i++)
813ae44829aSRadoslav Tsvetkov 		vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]);
81490cc4555SBryan O'Donoghue 	output->gen2.active_num = 0;
81590cc4555SBryan O'Donoghue 	spin_unlock_irqrestore(&vfe->output_lock, flags);
81690cc4555SBryan O'Donoghue 
81790cc4555SBryan O'Donoghue 	return vfe_reset(vfe);
81890cc4555SBryan O'Donoghue }
81990cc4555SBryan O'Donoghue 
82090cc4555SBryan O'Donoghue /*
82190cc4555SBryan O'Donoghue  * vfe_disable - Disable streaming on VFE line
82290cc4555SBryan O'Donoghue  * @line: VFE line
82390cc4555SBryan O'Donoghue  *
82490cc4555SBryan O'Donoghue  * Return 0 on success or a negative error code otherwise
82590cc4555SBryan O'Donoghue  */
vfe_disable(struct vfe_line * line)82690cc4555SBryan O'Donoghue int vfe_disable(struct vfe_line *line)
82790cc4555SBryan O'Donoghue {
82890cc4555SBryan O'Donoghue 	struct vfe_device *vfe = to_vfe(line);
8295c07f30bSBryan O'Donoghue 	int ret;
83090cc4555SBryan O'Donoghue 
8315c07f30bSBryan O'Donoghue 	ret = vfe_disable_output(line);
8325c07f30bSBryan O'Donoghue 	if (ret)
8335c07f30bSBryan O'Donoghue 		goto error;
83490cc4555SBryan O'Donoghue 
83590cc4555SBryan O'Donoghue 	vfe_put_output(line);
83690cc4555SBryan O'Donoghue 
83790cc4555SBryan O'Donoghue 	mutex_lock(&vfe->stream_lock);
83890cc4555SBryan O'Donoghue 
83990cc4555SBryan O'Donoghue 	vfe->stream_count--;
84090cc4555SBryan O'Donoghue 
84190cc4555SBryan O'Donoghue 	mutex_unlock(&vfe->stream_lock);
84290cc4555SBryan O'Donoghue 
8435c07f30bSBryan O'Donoghue error:
8445c07f30bSBryan O'Donoghue 	return ret;
84590cc4555SBryan O'Donoghue }
84690cc4555SBryan O'Donoghue 
847d8bdc3e4SRobert Foss /**
848d8bdc3e4SRobert Foss  * vfe_isr_comp_done() - Process composite image done interrupt
8499b5833f7STodor Tomov  * @vfe: VFE Device
8509b5833f7STodor Tomov  * @comp: Composite image id
8519b5833f7STodor Tomov  */
vfe_isr_comp_done(struct vfe_device * vfe,u8 comp)852633b388fSRobert Foss void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
8539b5833f7STodor Tomov {
8549b5833f7STodor Tomov 	unsigned int i;
8559b5833f7STodor Tomov 
8569b5833f7STodor Tomov 	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
8579b5833f7STodor Tomov 		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
858633b388fSRobert Foss 			vfe->isr_ops.wm_done(vfe, i);
8599b5833f7STodor Tomov 			break;
8609b5833f7STodor Tomov 		}
8619b5833f7STodor Tomov }
8629b5833f7STodor Tomov 
vfe_isr_reset_ack(struct vfe_device * vfe)863633b388fSRobert Foss void vfe_isr_reset_ack(struct vfe_device *vfe)
8644c98a5f5STodor Tomov {
8654c98a5f5STodor Tomov 	complete(&vfe->reset_complete);
8664c98a5f5STodor Tomov }
8674c98a5f5STodor Tomov 
868eb73faceSBryan O'Donoghue /*
869eb73faceSBryan O'Donoghue  * vfe_pm_domain_off - Disable power domains specific to this VFE.
870eb73faceSBryan O'Donoghue  * @vfe: VFE Device
871eb73faceSBryan O'Donoghue  */
vfe_pm_domain_off(struct vfe_device * vfe)872eb73faceSBryan O'Donoghue void vfe_pm_domain_off(struct vfe_device *vfe)
873eb73faceSBryan O'Donoghue {
874eb73faceSBryan O'Donoghue 	if (!vfe->genpd)
875eb73faceSBryan O'Donoghue 		return;
876eb73faceSBryan O'Donoghue 
877eb73faceSBryan O'Donoghue 	device_link_del(vfe->genpd_link);
878eb73faceSBryan O'Donoghue 	vfe->genpd_link = NULL;
879eb73faceSBryan O'Donoghue }
880eb73faceSBryan O'Donoghue 
881eb73faceSBryan O'Donoghue /*
882eb73faceSBryan O'Donoghue  * vfe_pm_domain_on - Enable power domains specific to this VFE.
883eb73faceSBryan O'Donoghue  * @vfe: VFE Device
884eb73faceSBryan O'Donoghue  */
vfe_pm_domain_on(struct vfe_device * vfe)885eb73faceSBryan O'Donoghue int vfe_pm_domain_on(struct vfe_device *vfe)
886eb73faceSBryan O'Donoghue {
887eb73faceSBryan O'Donoghue 	struct camss *camss = vfe->camss;
888eb73faceSBryan O'Donoghue 
889eb73faceSBryan O'Donoghue 	if (!vfe->genpd)
890eb73faceSBryan O'Donoghue 		return 0;
891eb73faceSBryan O'Donoghue 
892eb73faceSBryan O'Donoghue 	vfe->genpd_link = device_link_add(camss->dev, vfe->genpd,
893eb73faceSBryan O'Donoghue 					  DL_FLAG_STATELESS |
894eb73faceSBryan O'Donoghue 					  DL_FLAG_PM_RUNTIME |
895eb73faceSBryan O'Donoghue 					  DL_FLAG_RPM_ACTIVE);
896eb73faceSBryan O'Donoghue 	if (!vfe->genpd_link)
897eb73faceSBryan O'Donoghue 		return -EINVAL;
898eb73faceSBryan O'Donoghue 
899eb73faceSBryan O'Donoghue 	return 0;
900eb73faceSBryan O'Donoghue }
901eb73faceSBryan O'Donoghue 
vfe_match_clock_names(struct vfe_device * vfe,struct camss_clock * clock)902bcd2adfeSBryan O'Donoghue static int vfe_match_clock_names(struct vfe_device *vfe,
903bcd2adfeSBryan O'Donoghue 				 struct camss_clock *clock)
904bcd2adfeSBryan O'Donoghue {
905bcd2adfeSBryan O'Donoghue 	char vfe_name[7]; /* vfeXXX\0 */
906bcd2adfeSBryan O'Donoghue 	char vfe_lite_name[12]; /* vfe_liteXXX\0 */
907bcd2adfeSBryan O'Donoghue 
908bcd2adfeSBryan O'Donoghue 	snprintf(vfe_name, sizeof(vfe_name), "vfe%d", vfe->id);
909bcd2adfeSBryan O'Donoghue 	snprintf(vfe_lite_name, sizeof(vfe_lite_name), "vfe_lite%d", vfe->id);
910bcd2adfeSBryan O'Donoghue 
911bcd2adfeSBryan O'Donoghue 	return (!strcmp(clock->name, vfe_name) ||
912bcd2adfeSBryan O'Donoghue 		!strcmp(clock->name, vfe_lite_name) ||
913bcd2adfeSBryan O'Donoghue 		!strcmp(clock->name, "vfe_lite"));
914bcd2adfeSBryan O'Donoghue }
915bcd2adfeSBryan O'Donoghue 
9164c98a5f5STodor Tomov /*
917bbde3104STodor Tomov  * vfe_set_clock_rates - Calculate and set clock rates on VFE module
918bbde3104STodor Tomov  * @vfe: VFE device
919bbde3104STodor Tomov  *
920bbde3104STodor Tomov  * Return 0 on success or a negative error code otherwise
921bbde3104STodor Tomov  */
vfe_set_clock_rates(struct vfe_device * vfe)922bbde3104STodor Tomov static int vfe_set_clock_rates(struct vfe_device *vfe)
923bbde3104STodor Tomov {
9249c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
925633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
926bbde3104STodor Tomov 	int i, j;
927bbde3104STodor Tomov 	int ret;
928bbde3104STodor Tomov 
929ae44829aSRadoslav Tsvetkov 	for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
930bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
931bbde3104STodor Tomov 					    &pixel_clock[i]);
932bbde3104STodor Tomov 		if (ret)
933bbde3104STodor Tomov 			pixel_clock[i] = 0;
934bbde3104STodor Tomov 	}
935bbde3104STodor Tomov 
936bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
937bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
938bbde3104STodor Tomov 
939bcd2adfeSBryan O'Donoghue 		if (vfe_match_clock_names(vfe, clock)) {
940bbde3104STodor Tomov 			u64 min_rate = 0;
941bbde3104STodor Tomov 			long rate;
942bbde3104STodor Tomov 
943ae44829aSRadoslav Tsvetkov 			for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) {
944bbde3104STodor Tomov 				u32 tmp;
945bbde3104STodor Tomov 				u8 bpp;
946bbde3104STodor Tomov 
947bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
948bbde3104STodor Tomov 					tmp = pixel_clock[j];
949bbde3104STodor Tomov 				} else {
950cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
951cba3819dSTodor Tomov 
952ad458cb9SRadoslav Tsvetkov 					bpp = camss_format_get_bpp(l->formats,
953cba3819dSTodor Tomov 								   l->nformats,
954cba3819dSTodor Tomov 								   l->fmt[MSM_VFE_PAD_SINK].code);
955bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
956bbde3104STodor Tomov 				}
957bbde3104STodor Tomov 
958bbde3104STodor Tomov 				if (min_rate < tmp)
959bbde3104STodor Tomov 					min_rate = tmp;
960bbde3104STodor Tomov 			}
961bbde3104STodor Tomov 
962bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
963bbde3104STodor Tomov 
964bbde3104STodor Tomov 			for (j = 0; j < clock->nfreqs; j++)
965bbde3104STodor Tomov 				if (min_rate < clock->freq[j])
966bbde3104STodor Tomov 					break;
967bbde3104STodor Tomov 
968bbde3104STodor Tomov 			if (j == clock->nfreqs) {
969bbde3104STodor Tomov 				dev_err(dev,
970bbde3104STodor Tomov 					"Pixel clock is too high for VFE");
971bbde3104STodor Tomov 				return -EINVAL;
972bbde3104STodor Tomov 			}
973bbde3104STodor Tomov 
974bbde3104STodor Tomov 			/* if sensor pixel clock is not available */
975bbde3104STodor Tomov 			/* set highest possible VFE clock rate */
976bbde3104STodor Tomov 			if (min_rate == 0)
977bbde3104STodor Tomov 				j = clock->nfreqs - 1;
978bbde3104STodor Tomov 
979bbde3104STodor Tomov 			rate = clk_round_rate(clock->clk, clock->freq[j]);
980bbde3104STodor Tomov 			if (rate < 0) {
981bbde3104STodor Tomov 				dev_err(dev, "clk round rate failed: %ld\n",
982bbde3104STodor Tomov 					rate);
983bbde3104STodor Tomov 				return -EINVAL;
984bbde3104STodor Tomov 			}
985bbde3104STodor Tomov 
986bbde3104STodor Tomov 			ret = clk_set_rate(clock->clk, rate);
987bbde3104STodor Tomov 			if (ret < 0) {
988bbde3104STodor Tomov 				dev_err(dev, "clk set rate failed: %d\n", ret);
989bbde3104STodor Tomov 				return ret;
990bbde3104STodor Tomov 			}
991bbde3104STodor Tomov 		}
992bbde3104STodor Tomov 	}
993bbde3104STodor Tomov 
994bbde3104STodor Tomov 	return 0;
995bbde3104STodor Tomov }
996bbde3104STodor Tomov 
997bbde3104STodor Tomov /*
998bbde3104STodor Tomov  * vfe_check_clock_rates - Check current clock rates on VFE module
999bbde3104STodor Tomov  * @vfe: VFE device
1000bbde3104STodor Tomov  *
1001bbde3104STodor Tomov  * Return 0 if current clock rates are suitable for a new pipeline
1002bbde3104STodor Tomov  * or a negative error code otherwise
1003bbde3104STodor Tomov  */
vfe_check_clock_rates(struct vfe_device * vfe)1004bbde3104STodor Tomov static int vfe_check_clock_rates(struct vfe_device *vfe)
1005bbde3104STodor Tomov {
1006633b388fSRobert Foss 	u64 pixel_clock[VFE_LINE_NUM_MAX];
1007bbde3104STodor Tomov 	int i, j;
1008bbde3104STodor Tomov 	int ret;
1009bbde3104STodor Tomov 
1010ae44829aSRadoslav Tsvetkov 	for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
1011bbde3104STodor Tomov 		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
1012bbde3104STodor Tomov 					    &pixel_clock[i]);
1013bbde3104STodor Tomov 		if (ret)
1014bbde3104STodor Tomov 			pixel_clock[i] = 0;
1015bbde3104STodor Tomov 	}
1016bbde3104STodor Tomov 
1017bbde3104STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
1018bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
1019bbde3104STodor Tomov 
1020bcd2adfeSBryan O'Donoghue 		if (vfe_match_clock_names(vfe, clock)) {
1021bbde3104STodor Tomov 			u64 min_rate = 0;
1022bbde3104STodor Tomov 			unsigned long rate;
1023bbde3104STodor Tomov 
1024ae44829aSRadoslav Tsvetkov 			for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) {
1025bbde3104STodor Tomov 				u32 tmp;
1026bbde3104STodor Tomov 				u8 bpp;
1027bbde3104STodor Tomov 
1028bbde3104STodor Tomov 				if (j == VFE_LINE_PIX) {
1029bbde3104STodor Tomov 					tmp = pixel_clock[j];
1030bbde3104STodor Tomov 				} else {
1031cba3819dSTodor Tomov 					struct vfe_line *l = &vfe->line[j];
1032cba3819dSTodor Tomov 
1033ad458cb9SRadoslav Tsvetkov 					bpp = camss_format_get_bpp(l->formats,
1034cba3819dSTodor Tomov 								   l->nformats,
1035cba3819dSTodor Tomov 								   l->fmt[MSM_VFE_PAD_SINK].code);
1036bbde3104STodor Tomov 					tmp = pixel_clock[j] * bpp / 64;
1037bbde3104STodor Tomov 				}
1038bbde3104STodor Tomov 
1039bbde3104STodor Tomov 				if (min_rate < tmp)
1040bbde3104STodor Tomov 					min_rate = tmp;
1041bbde3104STodor Tomov 			}
1042bbde3104STodor Tomov 
1043bbde3104STodor Tomov 			camss_add_clock_margin(&min_rate);
1044bbde3104STodor Tomov 
1045bbde3104STodor Tomov 			rate = clk_get_rate(clock->clk);
1046bbde3104STodor Tomov 			if (rate < min_rate)
1047bbde3104STodor Tomov 				return -EBUSY;
1048bbde3104STodor Tomov 		}
1049bbde3104STodor Tomov 	}
1050bbde3104STodor Tomov 
1051bbde3104STodor Tomov 	return 0;
1052bbde3104STodor Tomov }
1053bbde3104STodor Tomov 
1054bbde3104STodor Tomov /*
10554c98a5f5STodor Tomov  * vfe_get - Power up and reset VFE module
10564c98a5f5STodor Tomov  * @vfe: VFE Device
10574c98a5f5STodor Tomov  *
10584c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
10594c98a5f5STodor Tomov  */
vfe_get(struct vfe_device * vfe)1060c5af8db8SBryan O'Donoghue int vfe_get(struct vfe_device *vfe)
10614c98a5f5STodor Tomov {
10624c98a5f5STodor Tomov 	int ret;
10634c98a5f5STodor Tomov 
10644c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
10654c98a5f5STodor Tomov 
10664c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
1067ae44829aSRadoslav Tsvetkov 		ret = vfe->res->hw_ops->pm_domain_on(vfe);
106802afa816STodor Tomov 		if (ret < 0)
106902afa816STodor Tomov 			goto error_pm_domain;
107002afa816STodor Tomov 
107109dfb36cSMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(vfe->camss->dev);
107202afa816STodor Tomov 		if (ret < 0)
107309dfb36cSMauro Carvalho Chehab 			goto error_domain_off;
107402afa816STodor Tomov 
1075bbde3104STodor Tomov 		ret = vfe_set_clock_rates(vfe);
1076bbde3104STodor Tomov 		if (ret < 0)
107777909691SDinghao Liu 			goto error_pm_runtime_get;
1078bbde3104STodor Tomov 
10794c98a5f5STodor Tomov 		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
10809c3e59deSTodor Tomov 					  vfe->camss->dev);
10814c98a5f5STodor Tomov 		if (ret < 0)
108277909691SDinghao Liu 			goto error_pm_runtime_get;
10834c98a5f5STodor Tomov 
10844c98a5f5STodor Tomov 		ret = vfe_reset(vfe);
10854c98a5f5STodor Tomov 		if (ret < 0)
10864c98a5f5STodor Tomov 			goto error_reset;
10874c98a5f5STodor Tomov 
10884c98a5f5STodor Tomov 		vfe_reset_output_maps(vfe);
10894c98a5f5STodor Tomov 
10904c98a5f5STodor Tomov 		vfe_init_outputs(vfe);
1091745b475eSRobert Foss 
1092ae44829aSRadoslav Tsvetkov 		vfe->res->hw_ops->hw_version(vfe);
1093bbde3104STodor Tomov 	} else {
1094bbde3104STodor Tomov 		ret = vfe_check_clock_rates(vfe);
1095bbde3104STodor Tomov 		if (ret < 0)
109626bda3daSBryan O'Donoghue 			goto error_pm_domain;
10974c98a5f5STodor Tomov 	}
10984c98a5f5STodor Tomov 	vfe->power_count++;
10994c98a5f5STodor Tomov 
11004c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
11014c98a5f5STodor Tomov 
11024c98a5f5STodor Tomov 	return 0;
11034c98a5f5STodor Tomov 
11044c98a5f5STodor Tomov error_reset:
11054c98a5f5STodor Tomov 	camss_disable_clocks(vfe->nclocks, vfe->clock);
11064c98a5f5STodor Tomov 
110702afa816STodor Tomov error_pm_runtime_get:
110877909691SDinghao Liu 	pm_runtime_put_sync(vfe->camss->dev);
110909dfb36cSMauro Carvalho Chehab error_domain_off:
1110ae44829aSRadoslav Tsvetkov 	vfe->res->hw_ops->pm_domain_off(vfe);
111102afa816STodor Tomov 
111202afa816STodor Tomov error_pm_domain:
11134c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
11144c98a5f5STodor Tomov 
11154c98a5f5STodor Tomov 	return ret;
11164c98a5f5STodor Tomov }
11174c98a5f5STodor Tomov 
11184c98a5f5STodor Tomov /*
11194c98a5f5STodor Tomov  * vfe_put - Power down VFE module
11204c98a5f5STodor Tomov  * @vfe: VFE Device
11214c98a5f5STodor Tomov  */
vfe_put(struct vfe_device * vfe)1122c5af8db8SBryan O'Donoghue void vfe_put(struct vfe_device *vfe)
11234c98a5f5STodor Tomov {
11244c98a5f5STodor Tomov 	mutex_lock(&vfe->power_lock);
11254c98a5f5STodor Tomov 
11264c98a5f5STodor Tomov 	if (vfe->power_count == 0) {
11279c3e59deSTodor Tomov 		dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
11284c98a5f5STodor Tomov 		goto exit;
11294c98a5f5STodor Tomov 	} else if (vfe->power_count == 1) {
11304c98a5f5STodor Tomov 		if (vfe->was_streaming) {
11314c98a5f5STodor Tomov 			vfe->was_streaming = 0;
1132ae44829aSRadoslav Tsvetkov 			vfe->res->hw_ops->vfe_halt(vfe);
11334c98a5f5STodor Tomov 		}
11344c98a5f5STodor Tomov 		camss_disable_clocks(vfe->nclocks, vfe->clock);
113502afa816STodor Tomov 		pm_runtime_put_sync(vfe->camss->dev);
1136ae44829aSRadoslav Tsvetkov 		vfe->res->hw_ops->pm_domain_off(vfe);
11374c98a5f5STodor Tomov 	}
11384c98a5f5STodor Tomov 
11394c98a5f5STodor Tomov 	vfe->power_count--;
11404c98a5f5STodor Tomov 
11414c98a5f5STodor Tomov exit:
11424c98a5f5STodor Tomov 	mutex_unlock(&vfe->power_lock);
11434c98a5f5STodor Tomov }
11444c98a5f5STodor Tomov 
11454c98a5f5STodor Tomov /*
11464c98a5f5STodor Tomov  * vfe_flush_buffers - Return all vb2 buffers
11474c98a5f5STodor Tomov  * @vid: Video device structure
11484c98a5f5STodor Tomov  * @state: vb2 buffer state of the returned buffers
11494c98a5f5STodor Tomov  *
11504c98a5f5STodor Tomov  * Return all buffers to vb2. This includes queued pending buffers (still
11514c98a5f5STodor Tomov  * unused) and any buffers given to the hardware but again still not used.
11524c98a5f5STodor Tomov  *
11534c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
11544c98a5f5STodor Tomov  */
vfe_flush_buffers(struct camss_video * vid,enum vb2_buffer_state state)1155633b388fSRobert Foss int vfe_flush_buffers(struct camss_video *vid,
11564c98a5f5STodor Tomov 		      enum vb2_buffer_state state)
11574c98a5f5STodor Tomov {
1158a93e5f4fSTodor Tomov 	struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
1159a93e5f4fSTodor Tomov 	struct vfe_device *vfe = to_vfe(line);
11604c98a5f5STodor Tomov 	struct vfe_output *output;
11614c98a5f5STodor Tomov 	unsigned long flags;
11624c98a5f5STodor Tomov 
11634c98a5f5STodor Tomov 	output = &line->output;
11644c98a5f5STodor Tomov 
11654c98a5f5STodor Tomov 	spin_lock_irqsave(&vfe->output_lock, flags);
11664c98a5f5STodor Tomov 
11674c98a5f5STodor Tomov 	vfe_buf_flush_pending(output, state);
11684c98a5f5STodor Tomov 
11694c98a5f5STodor Tomov 	if (output->buf[0])
11704c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
11714c98a5f5STodor Tomov 
11724c98a5f5STodor Tomov 	if (output->buf[1])
11734c98a5f5STodor Tomov 		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
11744c98a5f5STodor Tomov 
11754c98a5f5STodor Tomov 	if (output->last_buffer) {
11764c98a5f5STodor Tomov 		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
11774c98a5f5STodor Tomov 		output->last_buffer = NULL;
11784c98a5f5STodor Tomov 	}
11794c98a5f5STodor Tomov 
11804c98a5f5STodor Tomov 	spin_unlock_irqrestore(&vfe->output_lock, flags);
11814c98a5f5STodor Tomov 
11824c98a5f5STodor Tomov 	return 0;
11834c98a5f5STodor Tomov }
11844c98a5f5STodor Tomov 
11854c98a5f5STodor Tomov /*
11864c98a5f5STodor Tomov  * vfe_set_power - Power on/off VFE module
11874c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
11884c98a5f5STodor Tomov  * @on: Requested power state
11894c98a5f5STodor Tomov  *
11904c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
11914c98a5f5STodor Tomov  */
vfe_set_power(struct v4l2_subdev * sd,int on)11924c98a5f5STodor Tomov static int vfe_set_power(struct v4l2_subdev *sd, int on)
11934c98a5f5STodor Tomov {
11944c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
11954c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
11964c98a5f5STodor Tomov 	int ret;
11974c98a5f5STodor Tomov 
11984c98a5f5STodor Tomov 	if (on) {
11994c98a5f5STodor Tomov 		ret = vfe_get(vfe);
12004c98a5f5STodor Tomov 		if (ret < 0)
12014c98a5f5STodor Tomov 			return ret;
12024c98a5f5STodor Tomov 	} else {
12034c98a5f5STodor Tomov 		vfe_put(vfe);
12044c98a5f5STodor Tomov 	}
12054c98a5f5STodor Tomov 
12064c98a5f5STodor Tomov 	return 0;
12074c98a5f5STodor Tomov }
12084c98a5f5STodor Tomov 
12094c98a5f5STodor Tomov /*
12104c98a5f5STodor Tomov  * vfe_set_stream - Enable/disable streaming on VFE module
12114c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
12124c98a5f5STodor Tomov  * @enable: Requested streaming state
12134c98a5f5STodor Tomov  *
12144c98a5f5STodor Tomov  * Main configuration of VFE module is triggered here.
12154c98a5f5STodor Tomov  *
12164c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
12174c98a5f5STodor Tomov  */
vfe_set_stream(struct v4l2_subdev * sd,int enable)12184c98a5f5STodor Tomov static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
12194c98a5f5STodor Tomov {
12204c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
12214c98a5f5STodor Tomov 	struct vfe_device *vfe = to_vfe(line);
12224c98a5f5STodor Tomov 	int ret;
12234c98a5f5STodor Tomov 
12244c98a5f5STodor Tomov 	if (enable) {
12258ce158c1SMilen Mitkov 		line->output.state = VFE_OUTPUT_RESERVED;
1226ae44829aSRadoslav Tsvetkov 		ret = vfe->res->hw_ops->vfe_enable(line);
12274c98a5f5STodor Tomov 		if (ret < 0)
12289c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
12294c98a5f5STodor Tomov 				"Failed to enable vfe outputs\n");
12304c98a5f5STodor Tomov 	} else {
1231ae44829aSRadoslav Tsvetkov 		ret = vfe->res->hw_ops->vfe_disable(line);
12324c98a5f5STodor Tomov 		if (ret < 0)
12339c3e59deSTodor Tomov 			dev_err(vfe->camss->dev,
12344c98a5f5STodor Tomov 				"Failed to disable vfe outputs\n");
12354c98a5f5STodor Tomov 	}
12364c98a5f5STodor Tomov 
12374c98a5f5STodor Tomov 	return ret;
12384c98a5f5STodor Tomov }
12394c98a5f5STodor Tomov 
12404c98a5f5STodor Tomov /*
12414c98a5f5STodor Tomov  * __vfe_get_format - Get pointer to format structure
12424c98a5f5STodor Tomov  * @line: VFE line
1243c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
12444c98a5f5STodor Tomov  * @pad: pad from which format is requested
12454c98a5f5STodor Tomov  * @which: TRY or ACTIVE format
12464c98a5f5STodor Tomov  *
12474c98a5f5STodor Tomov  * Return pointer to TRY or ACTIVE format structure
12484c98a5f5STodor Tomov  */
12494c98a5f5STodor Tomov static struct v4l2_mbus_framefmt *
__vfe_get_format(struct vfe_line * line,struct v4l2_subdev_state * sd_state,unsigned int pad,enum v4l2_subdev_format_whence which)12504c98a5f5STodor Tomov __vfe_get_format(struct vfe_line *line,
12510d346d2aSTomi Valkeinen 		 struct v4l2_subdev_state *sd_state,
12524c98a5f5STodor Tomov 		 unsigned int pad,
12534c98a5f5STodor Tomov 		 enum v4l2_subdev_format_whence which)
12544c98a5f5STodor Tomov {
12554c98a5f5STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
1256bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_format(sd_state, pad);
12574c98a5f5STodor Tomov 
12584c98a5f5STodor Tomov 	return &line->fmt[pad];
12594c98a5f5STodor Tomov }
12604c98a5f5STodor Tomov 
1261810b6598STodor Tomov /*
1262810b6598STodor Tomov  * __vfe_get_compose - Get pointer to compose selection structure
1263810b6598STodor Tomov  * @line: VFE line
1264c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1265810b6598STodor Tomov  * @which: TRY or ACTIVE format
1266810b6598STodor Tomov  *
1267810b6598STodor Tomov  * Return pointer to TRY or ACTIVE compose rectangle structure
1268810b6598STodor Tomov  */
1269810b6598STodor Tomov static struct v4l2_rect *
__vfe_get_compose(struct vfe_line * line,struct v4l2_subdev_state * sd_state,enum v4l2_subdev_format_whence which)1270810b6598STodor Tomov __vfe_get_compose(struct vfe_line *line,
12710d346d2aSTomi Valkeinen 		  struct v4l2_subdev_state *sd_state,
1272810b6598STodor Tomov 		  enum v4l2_subdev_format_whence which)
1273810b6598STodor Tomov {
1274810b6598STodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
1275bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_compose(sd_state,
1276810b6598STodor Tomov 						     MSM_VFE_PAD_SINK);
1277810b6598STodor Tomov 
1278810b6598STodor Tomov 	return &line->compose;
1279810b6598STodor Tomov }
12804c98a5f5STodor Tomov 
12814c98a5f5STodor Tomov /*
1282780bf2feSTodor Tomov  * __vfe_get_crop - Get pointer to crop selection structure
1283780bf2feSTodor Tomov  * @line: VFE line
1284c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1285780bf2feSTodor Tomov  * @which: TRY or ACTIVE format
1286780bf2feSTodor Tomov  *
1287780bf2feSTodor Tomov  * Return pointer to TRY or ACTIVE crop rectangle structure
1288780bf2feSTodor Tomov  */
1289780bf2feSTodor Tomov static struct v4l2_rect *
__vfe_get_crop(struct vfe_line * line,struct v4l2_subdev_state * sd_state,enum v4l2_subdev_format_whence which)1290780bf2feSTodor Tomov __vfe_get_crop(struct vfe_line *line,
12910d346d2aSTomi Valkeinen 	       struct v4l2_subdev_state *sd_state,
1292780bf2feSTodor Tomov 	       enum v4l2_subdev_format_whence which)
1293780bf2feSTodor Tomov {
1294780bf2feSTodor Tomov 	if (which == V4L2_SUBDEV_FORMAT_TRY)
1295bc0e8d91SSakari Ailus 		return v4l2_subdev_state_get_crop(sd_state, MSM_VFE_PAD_SRC);
1296780bf2feSTodor Tomov 
1297780bf2feSTodor Tomov 	return &line->crop;
1298780bf2feSTodor Tomov }
1299780bf2feSTodor Tomov 
1300780bf2feSTodor Tomov /*
13014c98a5f5STodor Tomov  * vfe_try_format - Handle try format by pad subdev method
13024c98a5f5STodor Tomov  * @line: VFE line
1303c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
13044c98a5f5STodor Tomov  * @pad: pad on which format is requested
13054c98a5f5STodor Tomov  * @fmt: pointer to v4l2 format structure
13064c98a5f5STodor Tomov  * @which: wanted subdev format
13074c98a5f5STodor Tomov  */
vfe_try_format(struct vfe_line * line,struct v4l2_subdev_state * sd_state,unsigned int pad,struct v4l2_mbus_framefmt * fmt,enum v4l2_subdev_format_whence which)13084c98a5f5STodor Tomov static void vfe_try_format(struct vfe_line *line,
13090d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
13104c98a5f5STodor Tomov 			   unsigned int pad,
13114c98a5f5STodor Tomov 			   struct v4l2_mbus_framefmt *fmt,
13124c98a5f5STodor Tomov 			   enum v4l2_subdev_format_whence which)
13134c98a5f5STodor Tomov {
13144c98a5f5STodor Tomov 	unsigned int i;
13159b5833f7STodor Tomov 	u32 code;
13164c98a5f5STodor Tomov 
13174c98a5f5STodor Tomov 	switch (pad) {
13184c98a5f5STodor Tomov 	case MSM_VFE_PAD_SINK:
13194c98a5f5STodor Tomov 		/* Set format on sink pad */
13204c98a5f5STodor Tomov 
1321cba3819dSTodor Tomov 		for (i = 0; i < line->nformats; i++)
1322cba3819dSTodor Tomov 			if (fmt->code == line->formats[i].code)
13234c98a5f5STodor Tomov 				break;
13244c98a5f5STodor Tomov 
13254c98a5f5STodor Tomov 		/* If not found, use UYVY as default */
1326cba3819dSTodor Tomov 		if (i >= line->nformats)
132789936bfbSMartin Dørum 			fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
13284c98a5f5STodor Tomov 
13294c98a5f5STodor Tomov 		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
13304c98a5f5STodor Tomov 		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
13314c98a5f5STodor Tomov 
13324c98a5f5STodor Tomov 		fmt->field = V4L2_FIELD_NONE;
13334c98a5f5STodor Tomov 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
13344c98a5f5STodor Tomov 
13354c98a5f5STodor Tomov 		break;
13364c98a5f5STodor Tomov 
13374c98a5f5STodor Tomov 	case MSM_VFE_PAD_SRC:
13384c98a5f5STodor Tomov 		/* Set and return a format same as sink pad */
13399b5833f7STodor Tomov 		code = fmt->code;
13409b5833f7STodor Tomov 
13410d346d2aSTomi Valkeinen 		*fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
13420d346d2aSTomi Valkeinen 					 which);
134307eeb342STodor Tomov 
134407eeb342STodor Tomov 		fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
13454c98a5f5STodor Tomov 
1346810b6598STodor Tomov 		if (line->id == VFE_LINE_PIX) {
1347810b6598STodor Tomov 			struct v4l2_rect *rect;
1348810b6598STodor Tomov 
13490d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, which);
1350810b6598STodor Tomov 
1351810b6598STodor Tomov 			fmt->width = rect->width;
1352810b6598STodor Tomov 			fmt->height = rect->height;
1353810b6598STodor Tomov 		}
13549b5833f7STodor Tomov 
13554c98a5f5STodor Tomov 		break;
13564c98a5f5STodor Tomov 	}
13574c98a5f5STodor Tomov 
13584c98a5f5STodor Tomov 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
13594c98a5f5STodor Tomov }
13604c98a5f5STodor Tomov 
13614c98a5f5STodor Tomov /*
1362810b6598STodor Tomov  * vfe_try_compose - Handle try compose selection by pad subdev method
1363810b6598STodor Tomov  * @line: VFE line
1364c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1365810b6598STodor Tomov  * @rect: pointer to v4l2 rect structure
1366810b6598STodor Tomov  * @which: wanted subdev format
1367810b6598STodor Tomov  */
vfe_try_compose(struct vfe_line * line,struct v4l2_subdev_state * sd_state,struct v4l2_rect * rect,enum v4l2_subdev_format_whence which)1368810b6598STodor Tomov static void vfe_try_compose(struct vfe_line *line,
13690d346d2aSTomi Valkeinen 			    struct v4l2_subdev_state *sd_state,
1370810b6598STodor Tomov 			    struct v4l2_rect *rect,
1371810b6598STodor Tomov 			    enum v4l2_subdev_format_whence which)
1372810b6598STodor Tomov {
1373810b6598STodor Tomov 	struct v4l2_mbus_framefmt *fmt;
1374810b6598STodor Tomov 
13750d346d2aSTomi Valkeinen 	fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which);
1376810b6598STodor Tomov 
1377810b6598STodor Tomov 	if (rect->width > fmt->width)
1378810b6598STodor Tomov 		rect->width = fmt->width;
1379810b6598STodor Tomov 
1380810b6598STodor Tomov 	if (rect->height > fmt->height)
1381810b6598STodor Tomov 		rect->height = fmt->height;
1382810b6598STodor Tomov 
1383810b6598STodor Tomov 	if (fmt->width > rect->width * SCALER_RATIO_MAX)
1384810b6598STodor Tomov 		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
1385810b6598STodor Tomov 							SCALER_RATIO_MAX;
1386810b6598STodor Tomov 
1387810b6598STodor Tomov 	rect->width &= ~0x1;
1388810b6598STodor Tomov 
1389810b6598STodor Tomov 	if (fmt->height > rect->height * SCALER_RATIO_MAX)
1390810b6598STodor Tomov 		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
1391810b6598STodor Tomov 							SCALER_RATIO_MAX;
1392810b6598STodor Tomov 
1393810b6598STodor Tomov 	if (rect->width < 16)
1394810b6598STodor Tomov 		rect->width = 16;
1395810b6598STodor Tomov 
1396810b6598STodor Tomov 	if (rect->height < 4)
1397810b6598STodor Tomov 		rect->height = 4;
1398810b6598STodor Tomov }
1399810b6598STodor Tomov 
1400810b6598STodor Tomov /*
1401780bf2feSTodor Tomov  * vfe_try_crop - Handle try crop selection by pad subdev method
1402780bf2feSTodor Tomov  * @line: VFE line
1403c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1404780bf2feSTodor Tomov  * @rect: pointer to v4l2 rect structure
1405780bf2feSTodor Tomov  * @which: wanted subdev format
1406780bf2feSTodor Tomov  */
vfe_try_crop(struct vfe_line * line,struct v4l2_subdev_state * sd_state,struct v4l2_rect * rect,enum v4l2_subdev_format_whence which)1407780bf2feSTodor Tomov static void vfe_try_crop(struct vfe_line *line,
14080d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state,
1409780bf2feSTodor Tomov 			 struct v4l2_rect *rect,
1410780bf2feSTodor Tomov 			 enum v4l2_subdev_format_whence which)
1411780bf2feSTodor Tomov {
1412780bf2feSTodor Tomov 	struct v4l2_rect *compose;
1413780bf2feSTodor Tomov 
14140d346d2aSTomi Valkeinen 	compose = __vfe_get_compose(line, sd_state, which);
1415780bf2feSTodor Tomov 
1416780bf2feSTodor Tomov 	if (rect->width > compose->width)
1417780bf2feSTodor Tomov 		rect->width = compose->width;
1418780bf2feSTodor Tomov 
1419780bf2feSTodor Tomov 	if (rect->width + rect->left > compose->width)
1420780bf2feSTodor Tomov 		rect->left = compose->width - rect->width;
1421780bf2feSTodor Tomov 
1422780bf2feSTodor Tomov 	if (rect->height > compose->height)
1423780bf2feSTodor Tomov 		rect->height = compose->height;
1424780bf2feSTodor Tomov 
1425780bf2feSTodor Tomov 	if (rect->height + rect->top > compose->height)
1426780bf2feSTodor Tomov 		rect->top = compose->height - rect->height;
1427780bf2feSTodor Tomov 
1428780bf2feSTodor Tomov 	/* wm in line based mode writes multiple of 16 horizontally */
1429780bf2feSTodor Tomov 	rect->left += (rect->width & 0xf) >> 1;
1430780bf2feSTodor Tomov 	rect->width &= ~0xf;
1431780bf2feSTodor Tomov 
1432780bf2feSTodor Tomov 	if (rect->width < 16) {
1433780bf2feSTodor Tomov 		rect->left = 0;
1434780bf2feSTodor Tomov 		rect->width = 16;
1435780bf2feSTodor Tomov 	}
1436780bf2feSTodor Tomov 
1437780bf2feSTodor Tomov 	if (rect->height < 4) {
1438780bf2feSTodor Tomov 		rect->top = 0;
1439780bf2feSTodor Tomov 		rect->height = 4;
1440780bf2feSTodor Tomov 	}
1441780bf2feSTodor Tomov }
1442780bf2feSTodor Tomov 
1443780bf2feSTodor Tomov /*
14444c98a5f5STodor Tomov  * vfe_enum_mbus_code - Handle pixel format enumeration
14454c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1446c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
14474c98a5f5STodor Tomov  * @code: pointer to v4l2_subdev_mbus_code_enum structure
14484c98a5f5STodor Tomov  *
14494c98a5f5STodor Tomov  * return -EINVAL or zero on success
14504c98a5f5STodor Tomov  */
vfe_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)14514c98a5f5STodor Tomov static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
14520d346d2aSTomi Valkeinen 			      struct v4l2_subdev_state *sd_state,
14534c98a5f5STodor Tomov 			      struct v4l2_subdev_mbus_code_enum *code)
14544c98a5f5STodor Tomov {
14554c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
14564c98a5f5STodor Tomov 
14574c98a5f5STodor Tomov 	if (code->pad == MSM_VFE_PAD_SINK) {
1458cba3819dSTodor Tomov 		if (code->index >= line->nformats)
14594c98a5f5STodor Tomov 			return -EINVAL;
14604c98a5f5STodor Tomov 
1461cba3819dSTodor Tomov 		code->code = line->formats[code->index].code;
14624c98a5f5STodor Tomov 	} else {
146307eeb342STodor Tomov 		struct v4l2_mbus_framefmt *sink_fmt;
14644c98a5f5STodor Tomov 
14650d346d2aSTomi Valkeinen 		sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK,
14664c98a5f5STodor Tomov 					    code->which);
14674c98a5f5STodor Tomov 
146807eeb342STodor Tomov 		code->code = vfe_src_pad_code(line, sink_fmt->code,
146907eeb342STodor Tomov 					      code->index, 0);
147007eeb342STodor Tomov 		if (!code->code)
147107eeb342STodor Tomov 			return -EINVAL;
14724c98a5f5STodor Tomov 	}
14734c98a5f5STodor Tomov 
14744c98a5f5STodor Tomov 	return 0;
14754c98a5f5STodor Tomov }
14764c98a5f5STodor Tomov 
14774c98a5f5STodor Tomov /*
14784c98a5f5STodor Tomov  * vfe_enum_frame_size - Handle frame size enumeration
14794c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1480c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
14814c98a5f5STodor Tomov  * @fse: pointer to v4l2_subdev_frame_size_enum structure
14824c98a5f5STodor Tomov  *
14834c98a5f5STodor Tomov  * Return -EINVAL or zero on success
14844c98a5f5STodor Tomov  */
vfe_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)14854c98a5f5STodor Tomov static int vfe_enum_frame_size(struct v4l2_subdev *sd,
14860d346d2aSTomi Valkeinen 			       struct v4l2_subdev_state *sd_state,
14874c98a5f5STodor Tomov 			       struct v4l2_subdev_frame_size_enum *fse)
14884c98a5f5STodor Tomov {
14894c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
14904c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt format;
14914c98a5f5STodor Tomov 
14924c98a5f5STodor Tomov 	if (fse->index != 0)
14934c98a5f5STodor Tomov 		return -EINVAL;
14944c98a5f5STodor Tomov 
14954c98a5f5STodor Tomov 	format.code = fse->code;
14964c98a5f5STodor Tomov 	format.width = 1;
14974c98a5f5STodor Tomov 	format.height = 1;
14980d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
14994c98a5f5STodor Tomov 	fse->min_width = format.width;
15004c98a5f5STodor Tomov 	fse->min_height = format.height;
15014c98a5f5STodor Tomov 
15024c98a5f5STodor Tomov 	if (format.code != fse->code)
15034c98a5f5STodor Tomov 		return -EINVAL;
15044c98a5f5STodor Tomov 
15054c98a5f5STodor Tomov 	format.code = fse->code;
15064c98a5f5STodor Tomov 	format.width = -1;
15074c98a5f5STodor Tomov 	format.height = -1;
15080d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fse->pad, &format, fse->which);
15094c98a5f5STodor Tomov 	fse->max_width = format.width;
15104c98a5f5STodor Tomov 	fse->max_height = format.height;
15114c98a5f5STodor Tomov 
15124c98a5f5STodor Tomov 	return 0;
15134c98a5f5STodor Tomov }
15144c98a5f5STodor Tomov 
15154c98a5f5STodor Tomov /*
15164c98a5f5STodor Tomov  * vfe_get_format - Handle get format by pads subdev method
15174c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1518c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
15194c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
15204c98a5f5STodor Tomov  *
15214c98a5f5STodor Tomov  * Return -EINVAL or zero on success
15224c98a5f5STodor Tomov  */
vfe_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)15234c98a5f5STodor Tomov static int vfe_get_format(struct v4l2_subdev *sd,
15240d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
15254c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
15264c98a5f5STodor Tomov {
15274c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
15284c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
15294c98a5f5STodor Tomov 
15300d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
15314c98a5f5STodor Tomov 	if (format == NULL)
15324c98a5f5STodor Tomov 		return -EINVAL;
15334c98a5f5STodor Tomov 
15344c98a5f5STodor Tomov 	fmt->format = *format;
15354c98a5f5STodor Tomov 
15364c98a5f5STodor Tomov 	return 0;
15374c98a5f5STodor Tomov }
15384c98a5f5STodor Tomov 
1539810b6598STodor Tomov static int vfe_set_selection(struct v4l2_subdev *sd,
15400d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1541810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel);
1542810b6598STodor Tomov 
15434c98a5f5STodor Tomov /*
15444c98a5f5STodor Tomov  * vfe_set_format - Handle set format by pads subdev method
15454c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
1546c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
15474c98a5f5STodor Tomov  * @fmt: pointer to v4l2 subdev format structure
15484c98a5f5STodor Tomov  *
15494c98a5f5STodor Tomov  * Return -EINVAL or zero on success
15504c98a5f5STodor Tomov  */
vfe_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)15514c98a5f5STodor Tomov static int vfe_set_format(struct v4l2_subdev *sd,
15520d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
15534c98a5f5STodor Tomov 			  struct v4l2_subdev_format *fmt)
15544c98a5f5STodor Tomov {
15554c98a5f5STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
15564c98a5f5STodor Tomov 	struct v4l2_mbus_framefmt *format;
15574c98a5f5STodor Tomov 
15580d346d2aSTomi Valkeinen 	format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which);
15594c98a5f5STodor Tomov 	if (format == NULL)
15604c98a5f5STodor Tomov 		return -EINVAL;
15614c98a5f5STodor Tomov 
15620d346d2aSTomi Valkeinen 	vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which);
15634c98a5f5STodor Tomov 	*format = fmt->format;
15644c98a5f5STodor Tomov 
15654c98a5f5STodor Tomov 	if (fmt->pad == MSM_VFE_PAD_SINK) {
1566810b6598STodor Tomov 		struct v4l2_subdev_selection sel = { 0 };
1567810b6598STodor Tomov 		int ret;
1568810b6598STodor Tomov 
1569810b6598STodor Tomov 		/* Propagate the format from sink to source */
15700d346d2aSTomi Valkeinen 		format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC,
15714c98a5f5STodor Tomov 					  fmt->which);
15724c98a5f5STodor Tomov 
15734c98a5f5STodor Tomov 		*format = fmt->format;
15740d346d2aSTomi Valkeinen 		vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format,
15754c98a5f5STodor Tomov 			       fmt->which);
1576810b6598STodor Tomov 
1577810b6598STodor Tomov 		if (line->id != VFE_LINE_PIX)
1578810b6598STodor Tomov 			return 0;
1579810b6598STodor Tomov 
1580810b6598STodor Tomov 		/* Reset sink pad compose selection */
1581810b6598STodor Tomov 		sel.which = fmt->which;
1582810b6598STodor Tomov 		sel.pad = MSM_VFE_PAD_SINK;
1583810b6598STodor Tomov 		sel.target = V4L2_SEL_TGT_COMPOSE;
1584810b6598STodor Tomov 		sel.r.width = fmt->format.width;
1585810b6598STodor Tomov 		sel.r.height = fmt->format.height;
15860d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &sel);
1587810b6598STodor Tomov 		if (ret < 0)
1588810b6598STodor Tomov 			return ret;
15894c98a5f5STodor Tomov 	}
15904c98a5f5STodor Tomov 
15914c98a5f5STodor Tomov 	return 0;
15924c98a5f5STodor Tomov }
15934c98a5f5STodor Tomov 
15944c98a5f5STodor Tomov /*
1595810b6598STodor Tomov  * vfe_get_selection - Handle get selection by pads subdev method
1596810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1597c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1598810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1599810b6598STodor Tomov  *
1600810b6598STodor Tomov  * Return -EINVAL or zero on success
1601810b6598STodor Tomov  */
vfe_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)1602810b6598STodor Tomov static int vfe_get_selection(struct v4l2_subdev *sd,
16030d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1604810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1605810b6598STodor Tomov {
1606810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1607810b6598STodor Tomov 	struct v4l2_subdev_format fmt = { 0 };
1608780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1609810b6598STodor Tomov 	int ret;
1610810b6598STodor Tomov 
1611780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1612810b6598STodor Tomov 		return -EINVAL;
1613810b6598STodor Tomov 
1614780bf2feSTodor Tomov 	if (sel->pad == MSM_VFE_PAD_SINK)
1615810b6598STodor Tomov 		switch (sel->target) {
1616810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
1617810b6598STodor Tomov 			fmt.pad = sel->pad;
1618810b6598STodor Tomov 			fmt.which = sel->which;
16190d346d2aSTomi Valkeinen 			ret = vfe_get_format(sd, sd_state, &fmt);
1620810b6598STodor Tomov 			if (ret < 0)
1621810b6598STodor Tomov 				return ret;
1622780bf2feSTodor Tomov 
1623810b6598STodor Tomov 			sel->r.left = 0;
1624810b6598STodor Tomov 			sel->r.top = 0;
1625810b6598STodor Tomov 			sel->r.width = fmt.format.width;
1626810b6598STodor Tomov 			sel->r.height = fmt.format.height;
1627810b6598STodor Tomov 			break;
1628810b6598STodor Tomov 		case V4L2_SEL_TGT_COMPOSE:
16290d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1630780bf2feSTodor Tomov 			if (rect == NULL)
1631810b6598STodor Tomov 				return -EINVAL;
1632810b6598STodor Tomov 
1633780bf2feSTodor Tomov 			sel->r = *rect;
1634780bf2feSTodor Tomov 			break;
1635780bf2feSTodor Tomov 		default:
1636780bf2feSTodor Tomov 			return -EINVAL;
1637780bf2feSTodor Tomov 		}
1638780bf2feSTodor Tomov 	else if (sel->pad == MSM_VFE_PAD_SRC)
1639780bf2feSTodor Tomov 		switch (sel->target) {
1640780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP_BOUNDS:
16410d346d2aSTomi Valkeinen 			rect = __vfe_get_compose(line, sd_state, sel->which);
1642780bf2feSTodor Tomov 			if (rect == NULL)
1643780bf2feSTodor Tomov 				return -EINVAL;
1644780bf2feSTodor Tomov 
1645780bf2feSTodor Tomov 			sel->r.left = rect->left;
1646780bf2feSTodor Tomov 			sel->r.top = rect->top;
1647780bf2feSTodor Tomov 			sel->r.width = rect->width;
1648780bf2feSTodor Tomov 			sel->r.height = rect->height;
1649780bf2feSTodor Tomov 			break;
1650780bf2feSTodor Tomov 		case V4L2_SEL_TGT_CROP:
16510d346d2aSTomi Valkeinen 			rect = __vfe_get_crop(line, sd_state, sel->which);
1652780bf2feSTodor Tomov 			if (rect == NULL)
1653780bf2feSTodor Tomov 				return -EINVAL;
1654780bf2feSTodor Tomov 
1655780bf2feSTodor Tomov 			sel->r = *rect;
1656810b6598STodor Tomov 			break;
1657810b6598STodor Tomov 		default:
1658810b6598STodor Tomov 			return -EINVAL;
1659810b6598STodor Tomov 		}
1660810b6598STodor Tomov 
1661810b6598STodor Tomov 	return 0;
1662810b6598STodor Tomov }
1663810b6598STodor Tomov 
1664810b6598STodor Tomov /*
1665810b6598STodor Tomov  * vfe_set_selection - Handle set selection by pads subdev method
1666810b6598STodor Tomov  * @sd: VFE V4L2 subdevice
1667c1d96814SLaurent Pinchart  * @sd_state: V4L2 subdev state
1668810b6598STodor Tomov  * @sel: pointer to v4l2 subdev selection structure
1669810b6598STodor Tomov  *
1670810b6598STodor Tomov  * Return -EINVAL or zero on success
1671810b6598STodor Tomov  */
vfe_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)16729b62ccdbSColin Ian King static int vfe_set_selection(struct v4l2_subdev *sd,
16730d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
1674810b6598STodor Tomov 			     struct v4l2_subdev_selection *sel)
1675810b6598STodor Tomov {
1676810b6598STodor Tomov 	struct vfe_line *line = v4l2_get_subdevdata(sd);
1677780bf2feSTodor Tomov 	struct v4l2_rect *rect;
1678810b6598STodor Tomov 	int ret;
1679810b6598STodor Tomov 
1680780bf2feSTodor Tomov 	if (line->id != VFE_LINE_PIX)
1681810b6598STodor Tomov 		return -EINVAL;
1682810b6598STodor Tomov 
1683780bf2feSTodor Tomov 	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
1684780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SINK) {
1685780bf2feSTodor Tomov 		struct v4l2_subdev_selection crop = { 0 };
1686810b6598STodor Tomov 
16870d346d2aSTomi Valkeinen 		rect = __vfe_get_compose(line, sd_state, sel->which);
1688780bf2feSTodor Tomov 		if (rect == NULL)
1689810b6598STodor Tomov 			return -EINVAL;
1690810b6598STodor Tomov 
16910d346d2aSTomi Valkeinen 		vfe_try_compose(line, sd_state, &sel->r, sel->which);
1692780bf2feSTodor Tomov 		*rect = sel->r;
1693780bf2feSTodor Tomov 
1694780bf2feSTodor Tomov 		/* Reset source crop selection */
1695780bf2feSTodor Tomov 		crop.which = sel->which;
1696780bf2feSTodor Tomov 		crop.pad = MSM_VFE_PAD_SRC;
1697780bf2feSTodor Tomov 		crop.target = V4L2_SEL_TGT_CROP;
1698780bf2feSTodor Tomov 		crop.r = *rect;
16990d346d2aSTomi Valkeinen 		ret = vfe_set_selection(sd, sd_state, &crop);
1700780bf2feSTodor Tomov 	} else if (sel->target == V4L2_SEL_TGT_CROP &&
1701780bf2feSTodor Tomov 		sel->pad == MSM_VFE_PAD_SRC) {
1702780bf2feSTodor Tomov 		struct v4l2_subdev_format fmt = { 0 };
1703780bf2feSTodor Tomov 
17040d346d2aSTomi Valkeinen 		rect = __vfe_get_crop(line, sd_state, sel->which);
1705780bf2feSTodor Tomov 		if (rect == NULL)
1706780bf2feSTodor Tomov 			return -EINVAL;
1707780bf2feSTodor Tomov 
17080d346d2aSTomi Valkeinen 		vfe_try_crop(line, sd_state, &sel->r, sel->which);
1709780bf2feSTodor Tomov 		*rect = sel->r;
1710810b6598STodor Tomov 
1711810b6598STodor Tomov 		/* Reset source pad format width and height */
1712810b6598STodor Tomov 		fmt.which = sel->which;
1713810b6598STodor Tomov 		fmt.pad = MSM_VFE_PAD_SRC;
17140d346d2aSTomi Valkeinen 		ret = vfe_get_format(sd, sd_state, &fmt);
1715810b6598STodor Tomov 		if (ret < 0)
1716810b6598STodor Tomov 			return ret;
1717810b6598STodor Tomov 
1718780bf2feSTodor Tomov 		fmt.format.width = rect->width;
1719780bf2feSTodor Tomov 		fmt.format.height = rect->height;
17200d346d2aSTomi Valkeinen 		ret = vfe_set_format(sd, sd_state, &fmt);
1721780bf2feSTodor Tomov 	} else {
1722780bf2feSTodor Tomov 		ret = -EINVAL;
1723780bf2feSTodor Tomov 	}
1724810b6598STodor Tomov 
1725810b6598STodor Tomov 	return ret;
1726810b6598STodor Tomov }
1727810b6598STodor Tomov 
1728810b6598STodor Tomov /*
17294c98a5f5STodor Tomov  * vfe_init_formats - Initialize formats on all pads
17304c98a5f5STodor Tomov  * @sd: VFE V4L2 subdevice
17314c98a5f5STodor Tomov  * @fh: V4L2 subdev file handle
17324c98a5f5STodor Tomov  *
17334c98a5f5STodor Tomov  * Initialize all pad formats with default values.
17344c98a5f5STodor Tomov  *
17354c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
17364c98a5f5STodor Tomov  */
vfe_init_formats(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)17374c98a5f5STodor Tomov static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
17384c98a5f5STodor Tomov {
17394c98a5f5STodor Tomov 	struct v4l2_subdev_format format = {
17404c98a5f5STodor Tomov 		.pad = MSM_VFE_PAD_SINK,
17414c98a5f5STodor Tomov 		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
17424c98a5f5STodor Tomov 			      V4L2_SUBDEV_FORMAT_ACTIVE,
17434c98a5f5STodor Tomov 		.format = {
174489936bfbSMartin Dørum 			.code = MEDIA_BUS_FMT_UYVY8_1X16,
17454c98a5f5STodor Tomov 			.width = 1920,
17464c98a5f5STodor Tomov 			.height = 1080
17474c98a5f5STodor Tomov 		}
17484c98a5f5STodor Tomov 	};
17494c98a5f5STodor Tomov 
17500d346d2aSTomi Valkeinen 	return vfe_set_format(sd, fh ? fh->state : NULL, &format);
17514c98a5f5STodor Tomov }
17524c98a5f5STodor Tomov 
17534c98a5f5STodor Tomov /*
17544c98a5f5STodor Tomov  * msm_vfe_subdev_init - Initialize VFE device structure and resources
17554c98a5f5STodor Tomov  * @vfe: VFE device
17564c98a5f5STodor Tomov  * @res: VFE module resources table
17574c98a5f5STodor Tomov  *
17584c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
17594c98a5f5STodor Tomov  */
msm_vfe_subdev_init(struct camss * camss,struct vfe_device * vfe,const struct camss_subdev_resources * res,u8 id)17609c3e59deSTodor Tomov int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
17611643b787SBryan O'Donoghue 			const struct camss_subdev_resources *res, u8 id)
17624c98a5f5STodor Tomov {
17639c3e59deSTodor Tomov 	struct device *dev = camss->dev;
17644c98a5f5STodor Tomov 	struct platform_device *pdev = to_platform_device(dev);
1765bbde3104STodor Tomov 	int i, j;
17664c98a5f5STodor Tomov 	int ret;
17674c98a5f5STodor Tomov 
1768ae44829aSRadoslav Tsvetkov 	if (!res->vfe.line_num)
176991719b27SBryan O'Donoghue 		return -EINVAL;
177091719b27SBryan O'Donoghue 
1771ae44829aSRadoslav Tsvetkov 	vfe->res = &res->vfe;
1772ae44829aSRadoslav Tsvetkov 	vfe->res->hw_ops->subdev_init(dev, vfe);
1773ae44829aSRadoslav Tsvetkov 
1774d89751c6SBryan O'Donoghue 	/* Power domain */
1775d89751c6SBryan O'Donoghue 
1776ae44829aSRadoslav Tsvetkov 	if (res->vfe.pd_name) {
1777d89751c6SBryan O'Donoghue 		vfe->genpd = dev_pm_domain_attach_by_name(camss->dev,
1778ae44829aSRadoslav Tsvetkov 							  res->vfe.pd_name);
1779d89751c6SBryan O'Donoghue 		if (IS_ERR(vfe->genpd)) {
1780d89751c6SBryan O'Donoghue 			ret = PTR_ERR(vfe->genpd);
1781d89751c6SBryan O'Donoghue 			return ret;
1782d89751c6SBryan O'Donoghue 		}
1783d89751c6SBryan O'Donoghue 	}
1784d89751c6SBryan O'Donoghue 
1785ae44829aSRadoslav Tsvetkov 	if (!vfe->genpd && res->vfe.has_pd) {
1786d89751c6SBryan O'Donoghue 		/*
1787d89751c6SBryan O'Donoghue 		 * Legacy magic index.
1788d89751c6SBryan O'Donoghue 		 * Requires
1789d89751c6SBryan O'Donoghue 		 * power-domain = <VFE_X>,
1790d89751c6SBryan O'Donoghue 		 *                <VFE_Y>,
1791d89751c6SBryan O'Donoghue 		 *                <TITAN_TOP>
1792d89751c6SBryan O'Donoghue 		 * id must correspondng to the index of the VFE which must
1793d89751c6SBryan O'Donoghue 		 * come before the TOP GDSC. VFE Lite has no individually
1794d89751c6SBryan O'Donoghue 		 * collapasible domain which is why id < vfe_num is a valid
1795d89751c6SBryan O'Donoghue 		 * check.
1796d89751c6SBryan O'Donoghue 		 */
179723aa4f0cSBryan O'Donoghue 		vfe->genpd = dev_pm_domain_attach_by_id(camss->dev, id);
179823aa4f0cSBryan O'Donoghue 		if (IS_ERR(vfe->genpd))
179923aa4f0cSBryan O'Donoghue 			return PTR_ERR(vfe->genpd);
180023aa4f0cSBryan O'Donoghue 	}
1801a409b3f0SBryan O'Donoghue 
18024c98a5f5STodor Tomov 	/* Memory */
18034c98a5f5STodor Tomov 
1804414e0a64Sdingsenjie 	vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
18054c98a5f5STodor Tomov 	if (IS_ERR(vfe->base)) {
18064c98a5f5STodor Tomov 		dev_err(dev, "could not map memory\n");
18074c98a5f5STodor Tomov 		return PTR_ERR(vfe->base);
18084c98a5f5STodor Tomov 	}
18094c98a5f5STodor Tomov 
18104c98a5f5STodor Tomov 	/* Interrupt */
18114c98a5f5STodor Tomov 
1812b416be3aSLad Prabhakar 	ret = platform_get_irq_byname(pdev, res->interrupt[0]);
1813b416be3aSLad Prabhakar 	if (ret < 0)
1814b416be3aSLad Prabhakar 		return ret;
18154c98a5f5STodor Tomov 
1816b416be3aSLad Prabhakar 	vfe->irq = ret;
18174c98a5f5STodor Tomov 	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
1818ed38a146SJonathan Marek 		 dev_name(dev), MSM_VFE_NAME, id);
1819ae44829aSRadoslav Tsvetkov 	ret = devm_request_irq(dev, vfe->irq, vfe->res->hw_ops->isr,
18204c98a5f5STodor Tomov 			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
18214c98a5f5STodor Tomov 	if (ret < 0) {
18224c98a5f5STodor Tomov 		dev_err(dev, "request_irq failed: %d\n", ret);
18234c98a5f5STodor Tomov 		return ret;
18244c98a5f5STodor Tomov 	}
18254c98a5f5STodor Tomov 
18264c98a5f5STodor Tomov 	/* Clocks */
18274c98a5f5STodor Tomov 
18284c98a5f5STodor Tomov 	vfe->nclocks = 0;
18294c98a5f5STodor Tomov 	while (res->clock[vfe->nclocks])
18304c98a5f5STodor Tomov 		vfe->nclocks++;
18314c98a5f5STodor Tomov 
1832a86854d0SKees Cook 	vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
18334c98a5f5STodor Tomov 				  GFP_KERNEL);
18344c98a5f5STodor Tomov 	if (!vfe->clock)
18354c98a5f5STodor Tomov 		return -ENOMEM;
18364c98a5f5STodor Tomov 
18374c98a5f5STodor Tomov 	for (i = 0; i < vfe->nclocks; i++) {
1838bbde3104STodor Tomov 		struct camss_clock *clock = &vfe->clock[i];
18394c98a5f5STodor Tomov 
1840bbde3104STodor Tomov 		clock->clk = devm_clk_get(dev, res->clock[i]);
1841bbde3104STodor Tomov 		if (IS_ERR(clock->clk))
1842bbde3104STodor Tomov 			return PTR_ERR(clock->clk);
1843bbde3104STodor Tomov 
1844bbde3104STodor Tomov 		clock->name = res->clock[i];
1845bbde3104STodor Tomov 
1846bbde3104STodor Tomov 		clock->nfreqs = 0;
1847bbde3104STodor Tomov 		while (res->clock_rate[i][clock->nfreqs])
1848bbde3104STodor Tomov 			clock->nfreqs++;
1849bbde3104STodor Tomov 
1850bbde3104STodor Tomov 		if (!clock->nfreqs) {
1851bbde3104STodor Tomov 			clock->freq = NULL;
1852bbde3104STodor Tomov 			continue;
18534c98a5f5STodor Tomov 		}
1854bbde3104STodor Tomov 
1855a86854d0SKees Cook 		clock->freq = devm_kcalloc(dev,
1856a86854d0SKees Cook 					   clock->nfreqs,
1857a86854d0SKees Cook 					   sizeof(*clock->freq),
1858a86854d0SKees Cook 					   GFP_KERNEL);
1859bbde3104STodor Tomov 		if (!clock->freq)
1860bbde3104STodor Tomov 			return -ENOMEM;
1861bbde3104STodor Tomov 
1862bbde3104STodor Tomov 		for (j = 0; j < clock->nfreqs; j++)
1863bbde3104STodor Tomov 			clock->freq[j] = res->clock_rate[i][j];
18644c98a5f5STodor Tomov 	}
18654c98a5f5STodor Tomov 
18664c98a5f5STodor Tomov 	mutex_init(&vfe->power_lock);
18674c98a5f5STodor Tomov 	vfe->power_count = 0;
18684c98a5f5STodor Tomov 
18694c98a5f5STodor Tomov 	mutex_init(&vfe->stream_lock);
18704c98a5f5STodor Tomov 	vfe->stream_count = 0;
18714c98a5f5STodor Tomov 
18724c98a5f5STodor Tomov 	spin_lock_init(&vfe->output_lock);
18734c98a5f5STodor Tomov 
18749c3e59deSTodor Tomov 	vfe->camss = camss;
18759c3e59deSTodor Tomov 	vfe->id = id;
18764c98a5f5STodor Tomov 	vfe->reg_update = 0;
18774c98a5f5STodor Tomov 
1878ae44829aSRadoslav Tsvetkov 	for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
1879cba3819dSTodor Tomov 		struct vfe_line *l = &vfe->line[i];
1880cba3819dSTodor Tomov 
1881cba3819dSTodor Tomov 		l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1882cba3819dSTodor Tomov 		l->video_out.camss = camss;
1883cba3819dSTodor Tomov 		l->id = i;
1884cba3819dSTodor Tomov 		init_completion(&l->output.sof);
1885cba3819dSTodor Tomov 		init_completion(&l->output.reg_update);
1886cba3819dSTodor Tomov 
1887cba3819dSTodor Tomov 		if (i == VFE_LINE_PIX) {
18886c46cb0dSRadoslav Tsvetkov 			l->nformats = res->vfe.formats_pix->nformats;
18896c46cb0dSRadoslav Tsvetkov 			l->formats = res->vfe.formats_pix->formats;
1890cba3819dSTodor Tomov 		} else {
18916c46cb0dSRadoslav Tsvetkov 			l->nformats = res->vfe.formats_rdi->nformats;
18926c46cb0dSRadoslav Tsvetkov 			l->formats = res->vfe.formats_rdi->formats;
1893cba3819dSTodor Tomov 		}
18944c98a5f5STodor Tomov 	}
18954c98a5f5STodor Tomov 
18964c98a5f5STodor Tomov 	init_completion(&vfe->reset_complete);
18974c98a5f5STodor Tomov 	init_completion(&vfe->halt_complete);
18984c98a5f5STodor Tomov 
18994c98a5f5STodor Tomov 	return 0;
19004c98a5f5STodor Tomov }
19014c98a5f5STodor Tomov 
19024c98a5f5STodor Tomov /*
190323aa4f0cSBryan O'Donoghue  * msm_vfe_genpd_cleanup - Cleanup VFE genpd linkages
190423aa4f0cSBryan O'Donoghue  * @vfe: VFE device
190523aa4f0cSBryan O'Donoghue  */
msm_vfe_genpd_cleanup(struct vfe_device * vfe)190623aa4f0cSBryan O'Donoghue void msm_vfe_genpd_cleanup(struct vfe_device *vfe)
190723aa4f0cSBryan O'Donoghue {
190823aa4f0cSBryan O'Donoghue 	if (vfe->genpd_link)
190923aa4f0cSBryan O'Donoghue 		device_link_del(vfe->genpd_link);
191023aa4f0cSBryan O'Donoghue 
191123aa4f0cSBryan O'Donoghue 	if (vfe->genpd)
191223aa4f0cSBryan O'Donoghue 		dev_pm_domain_detach(vfe->genpd, true);
191323aa4f0cSBryan O'Donoghue }
191423aa4f0cSBryan O'Donoghue 
191523aa4f0cSBryan O'Donoghue /*
19164c98a5f5STodor Tomov  * vfe_link_setup - Setup VFE connections
19174c98a5f5STodor Tomov  * @entity: Pointer to media entity structure
19184c98a5f5STodor Tomov  * @local: Pointer to local pad
19194c98a5f5STodor Tomov  * @remote: Pointer to remote pad
19204c98a5f5STodor Tomov  * @flags: Link flags
19214c98a5f5STodor Tomov  *
19224c98a5f5STodor Tomov  * Return 0 on success
19234c98a5f5STodor Tomov  */
vfe_link_setup(struct media_entity * entity,const struct media_pad * local,const struct media_pad * remote,u32 flags)19244c98a5f5STodor Tomov static int vfe_link_setup(struct media_entity *entity,
19254c98a5f5STodor Tomov 			  const struct media_pad *local,
19264c98a5f5STodor Tomov 			  const struct media_pad *remote, u32 flags)
19274c98a5f5STodor Tomov {
19284c98a5f5STodor Tomov 	if (flags & MEDIA_LNK_FL_ENABLED)
1929b2e44430SLaurent Pinchart 		if (media_pad_remote_pad_first(local))
19304c98a5f5STodor Tomov 			return -EBUSY;
19314c98a5f5STodor Tomov 
19324c98a5f5STodor Tomov 	return 0;
19334c98a5f5STodor Tomov }
19344c98a5f5STodor Tomov 
19354c98a5f5STodor Tomov static const struct v4l2_subdev_core_ops vfe_core_ops = {
19364c98a5f5STodor Tomov 	.s_power = vfe_set_power,
19374c98a5f5STodor Tomov };
19384c98a5f5STodor Tomov 
19394c98a5f5STodor Tomov static const struct v4l2_subdev_video_ops vfe_video_ops = {
19404c98a5f5STodor Tomov 	.s_stream = vfe_set_stream,
19414c98a5f5STodor Tomov };
19424c98a5f5STodor Tomov 
19434c98a5f5STodor Tomov static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
19444c98a5f5STodor Tomov 	.enum_mbus_code = vfe_enum_mbus_code,
19454c98a5f5STodor Tomov 	.enum_frame_size = vfe_enum_frame_size,
19464c98a5f5STodor Tomov 	.get_fmt = vfe_get_format,
19474c98a5f5STodor Tomov 	.set_fmt = vfe_set_format,
1948810b6598STodor Tomov 	.get_selection = vfe_get_selection,
1949810b6598STodor Tomov 	.set_selection = vfe_set_selection,
19504c98a5f5STodor Tomov };
19514c98a5f5STodor Tomov 
19524c98a5f5STodor Tomov static const struct v4l2_subdev_ops vfe_v4l2_ops = {
19534c98a5f5STodor Tomov 	.core = &vfe_core_ops,
19544c98a5f5STodor Tomov 	.video = &vfe_video_ops,
19554c98a5f5STodor Tomov 	.pad = &vfe_pad_ops,
19564c98a5f5STodor Tomov };
19574c98a5f5STodor Tomov 
19584c98a5f5STodor Tomov static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
19594c98a5f5STodor Tomov 	.open = vfe_init_formats,
19604c98a5f5STodor Tomov };
19614c98a5f5STodor Tomov 
19624c98a5f5STodor Tomov static const struct media_entity_operations vfe_media_ops = {
19634c98a5f5STodor Tomov 	.link_setup = vfe_link_setup,
19644c98a5f5STodor Tomov 	.link_validate = v4l2_subdev_link_validate,
19654c98a5f5STodor Tomov };
19664c98a5f5STodor Tomov 
vfe_bpl_align(struct vfe_device * vfe)1967f2a7ffabSBryan O'Donoghue static int vfe_bpl_align(struct vfe_device *vfe)
1968f2a7ffabSBryan O'Donoghue {
1969f2a7ffabSBryan O'Donoghue 	int ret = 8;
1970f2a7ffabSBryan O'Donoghue 
1971f2a7ffabSBryan O'Donoghue 	switch (vfe->camss->res->version) {
1972a7f5b36bSSuresh Vankadara 	case CAMSS_7280:
1973f2a7ffabSBryan O'Donoghue 	case CAMSS_8250:
1974f2a7ffabSBryan O'Donoghue 	case CAMSS_8280XP:
19753522673bSVikram Sharma 	case CAMSS_845:
197639e3f5bcSDepeng Shao 	case CAMSS_8550:
1977*1830cf0fSBryan O'Donoghue 	case CAMSS_X1E80100:
1978f2a7ffabSBryan O'Donoghue 		ret = 16;
1979f2a7ffabSBryan O'Donoghue 		break;
1980f2a7ffabSBryan O'Donoghue 	default:
1981f2a7ffabSBryan O'Donoghue 		break;
1982f2a7ffabSBryan O'Donoghue 	}
1983f2a7ffabSBryan O'Donoghue 
1984f2a7ffabSBryan O'Donoghue 	return ret;
1985f2a7ffabSBryan O'Donoghue }
1986f2a7ffabSBryan O'Donoghue 
19874c98a5f5STodor Tomov /*
19884c98a5f5STodor Tomov  * msm_vfe_register_entities - Register subdev node for VFE module
19894c98a5f5STodor Tomov  * @vfe: VFE device
19904c98a5f5STodor Tomov  * @v4l2_dev: V4L2 device
19914c98a5f5STodor Tomov  *
19924c98a5f5STodor Tomov  * Initialize and register a subdev node for the VFE module. Then
19934c98a5f5STodor Tomov  * call msm_video_register() to register the video device node which
19944c98a5f5STodor Tomov  * will be connected to this subdev node. Then actually create the
19954c98a5f5STodor Tomov  * media link between them.
19964c98a5f5STodor Tomov  *
19974c98a5f5STodor Tomov  * Return 0 on success or a negative error code otherwise
19984c98a5f5STodor Tomov  */
msm_vfe_register_entities(struct vfe_device * vfe,struct v4l2_device * v4l2_dev)19994c98a5f5STodor Tomov int msm_vfe_register_entities(struct vfe_device *vfe,
20004c98a5f5STodor Tomov 			      struct v4l2_device *v4l2_dev)
20014c98a5f5STodor Tomov {
20029c3e59deSTodor Tomov 	struct device *dev = vfe->camss->dev;
20034c98a5f5STodor Tomov 	struct v4l2_subdev *sd;
20044c98a5f5STodor Tomov 	struct media_pad *pads;
20054c98a5f5STodor Tomov 	struct camss_video *video_out;
20064c98a5f5STodor Tomov 	int ret;
20074c98a5f5STodor Tomov 	int i;
20084c98a5f5STodor Tomov 
2009ae44829aSRadoslav Tsvetkov 	for (i = 0; i < vfe->res->line_num; i++) {
20104c98a5f5STodor Tomov 		char name[32];
20114c98a5f5STodor Tomov 
20124c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
20134c98a5f5STodor Tomov 		pads = vfe->line[i].pads;
20144c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
20154c98a5f5STodor Tomov 
20164c98a5f5STodor Tomov 		v4l2_subdev_init(sd, &vfe_v4l2_ops);
20174c98a5f5STodor Tomov 		sd->internal_ops = &vfe_v4l2_internal_ops;
20184c98a5f5STodor Tomov 		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
20199b5833f7STodor Tomov 		if (i == VFE_LINE_PIX)
20209b5833f7STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
20219b5833f7STodor Tomov 				 MSM_VFE_NAME, vfe->id, "pix");
20229b5833f7STodor Tomov 		else
20234c98a5f5STodor Tomov 			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
20244c98a5f5STodor Tomov 				 MSM_VFE_NAME, vfe->id, "rdi", i);
20259b5833f7STodor Tomov 
20264c98a5f5STodor Tomov 		v4l2_set_subdevdata(sd, &vfe->line[i]);
20274c98a5f5STodor Tomov 
20284c98a5f5STodor Tomov 		ret = vfe_init_formats(sd, NULL);
20294c98a5f5STodor Tomov 		if (ret < 0) {
20304c98a5f5STodor Tomov 			dev_err(dev, "Failed to init format: %d\n", ret);
20314c98a5f5STodor Tomov 			goto error_init;
20324c98a5f5STodor Tomov 		}
20334c98a5f5STodor Tomov 
20344c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
20354c98a5f5STodor Tomov 		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
20364c98a5f5STodor Tomov 
20374c98a5f5STodor Tomov 		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
20384c98a5f5STodor Tomov 		sd->entity.ops = &vfe_media_ops;
20394c98a5f5STodor Tomov 		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
20404c98a5f5STodor Tomov 					     pads);
20414c98a5f5STodor Tomov 		if (ret < 0) {
20424c98a5f5STodor Tomov 			dev_err(dev, "Failed to init media entity: %d\n", ret);
20434c98a5f5STodor Tomov 			goto error_init;
20444c98a5f5STodor Tomov 		}
20454c98a5f5STodor Tomov 
20464c98a5f5STodor Tomov 		ret = v4l2_device_register_subdev(v4l2_dev, sd);
20474c98a5f5STodor Tomov 		if (ret < 0) {
20484c98a5f5STodor Tomov 			dev_err(dev, "Failed to register subdev: %d\n", ret);
20494c98a5f5STodor Tomov 			goto error_reg_subdev;
20504c98a5f5STodor Tomov 		}
20514c98a5f5STodor Tomov 
2052633b388fSRobert Foss 		video_out->ops = &vfe->video_ops;
2053f2a7ffabSBryan O'Donoghue 		video_out->bpl_alignment = vfe_bpl_align(vfe);
20547b4aff6fSTodor Tomov 		video_out->line_based = 0;
20557b4aff6fSTodor Tomov 		if (i == VFE_LINE_PIX) {
20567b4aff6fSTodor Tomov 			video_out->bpl_alignment = 16;
20577b4aff6fSTodor Tomov 			video_out->line_based = 1;
20587b4aff6fSTodor Tomov 		}
20596c46cb0dSRadoslav Tsvetkov 
20606c46cb0dSRadoslav Tsvetkov 		video_out->nformats = vfe->line[i].nformats;
20616c46cb0dSRadoslav Tsvetkov 		video_out->formats = vfe->line[i].formats;
20626c46cb0dSRadoslav Tsvetkov 
20634c98a5f5STodor Tomov 		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
20644c98a5f5STodor Tomov 			 MSM_VFE_NAME, vfe->id, "video", i);
20656c46cb0dSRadoslav Tsvetkov 		ret = msm_video_register(video_out, v4l2_dev, name);
20664c98a5f5STodor Tomov 		if (ret < 0) {
20674c98a5f5STodor Tomov 			dev_err(dev, "Failed to register video node: %d\n",
20684c98a5f5STodor Tomov 				ret);
20694c98a5f5STodor Tomov 			goto error_reg_video;
20704c98a5f5STodor Tomov 		}
20714c98a5f5STodor Tomov 
20724c98a5f5STodor Tomov 		ret = media_create_pad_link(
20734c98a5f5STodor Tomov 				&sd->entity, MSM_VFE_PAD_SRC,
20744c98a5f5STodor Tomov 				&video_out->vdev.entity, 0,
20754c98a5f5STodor Tomov 				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
20764c98a5f5STodor Tomov 		if (ret < 0) {
20774c98a5f5STodor Tomov 			dev_err(dev, "Failed to link %s->%s entities: %d\n",
20784c98a5f5STodor Tomov 				sd->entity.name, video_out->vdev.entity.name,
20794c98a5f5STodor Tomov 				ret);
20804c98a5f5STodor Tomov 			goto error_link;
20814c98a5f5STodor Tomov 		}
20824c98a5f5STodor Tomov 	}
20834c98a5f5STodor Tomov 
20844c98a5f5STodor Tomov 	return 0;
20854c98a5f5STodor Tomov 
20864c98a5f5STodor Tomov error_link:
20874c98a5f5STodor Tomov 	msm_video_unregister(video_out);
20884c98a5f5STodor Tomov 
20894c98a5f5STodor Tomov error_reg_video:
20904c98a5f5STodor Tomov 	v4l2_device_unregister_subdev(sd);
20914c98a5f5STodor Tomov 
20924c98a5f5STodor Tomov error_reg_subdev:
20934c98a5f5STodor Tomov 	media_entity_cleanup(&sd->entity);
20944c98a5f5STodor Tomov 
20954c98a5f5STodor Tomov error_init:
20964c98a5f5STodor Tomov 	for (i--; i >= 0; i--) {
20974c98a5f5STodor Tomov 		sd = &vfe->line[i].subdev;
20984c98a5f5STodor Tomov 		video_out = &vfe->line[i].video_out;
20994c98a5f5STodor Tomov 
21004c98a5f5STodor Tomov 		msm_video_unregister(video_out);
21014c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
21024c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
21034c98a5f5STodor Tomov 	}
21044c98a5f5STodor Tomov 
21054c98a5f5STodor Tomov 	return ret;
21064c98a5f5STodor Tomov }
21074c98a5f5STodor Tomov 
21084c98a5f5STodor Tomov /*
21094c98a5f5STodor Tomov  * msm_vfe_unregister_entities - Unregister VFE module subdev node
21104c98a5f5STodor Tomov  * @vfe: VFE device
21114c98a5f5STodor Tomov  */
msm_vfe_unregister_entities(struct vfe_device * vfe)21124c98a5f5STodor Tomov void msm_vfe_unregister_entities(struct vfe_device *vfe)
21134c98a5f5STodor Tomov {
21144c98a5f5STodor Tomov 	int i;
21154c98a5f5STodor Tomov 
21164c98a5f5STodor Tomov 	mutex_destroy(&vfe->power_lock);
21174c98a5f5STodor Tomov 	mutex_destroy(&vfe->stream_lock);
21184c98a5f5STodor Tomov 
2119ae44829aSRadoslav Tsvetkov 	for (i = 0; i < vfe->res->line_num; i++) {
21204c98a5f5STodor Tomov 		struct v4l2_subdev *sd = &vfe->line[i].subdev;
21214c98a5f5STodor Tomov 		struct camss_video *video_out = &vfe->line[i].video_out;
21224c98a5f5STodor Tomov 
21234c98a5f5STodor Tomov 		msm_video_unregister(video_out);
21244c98a5f5STodor Tomov 		v4l2_device_unregister_subdev(sd);
21254c98a5f5STodor Tomov 		media_entity_cleanup(&sd->entity);
21264c98a5f5STodor Tomov 	}
21274c98a5f5STodor Tomov }
21286997278aSMatti Lehtimäki 
vfe_is_lite(struct vfe_device * vfe)21296997278aSMatti Lehtimäki bool vfe_is_lite(struct vfe_device *vfe)
21306997278aSMatti Lehtimäki {
2131ae44829aSRadoslav Tsvetkov 	return vfe->camss->res->vfe_res[vfe->id].vfe.is_lite;
21326997278aSMatti Lehtimäki }
2133