1f5fbb83fSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
29d4fa1a1SMauro Carvalho Chehab /*
39d4fa1a1SMauro Carvalho Chehab  * Support for Medifield PNW Camera Imaging ISP subsystem.
49d4fa1a1SMauro Carvalho Chehab  *
59d4fa1a1SMauro Carvalho Chehab  * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
69d4fa1a1SMauro Carvalho Chehab  *
79d4fa1a1SMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
89d4fa1a1SMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License version
99d4fa1a1SMauro Carvalho Chehab  * 2 as published by the Free Software Foundation.
109d4fa1a1SMauro Carvalho Chehab  *
119d4fa1a1SMauro Carvalho Chehab  * This program is distributed in the hope that it will be useful,
129d4fa1a1SMauro Carvalho Chehab  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139d4fa1a1SMauro Carvalho Chehab  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149d4fa1a1SMauro Carvalho Chehab  * GNU General Public License for more details.
159d4fa1a1SMauro Carvalho Chehab  *
169d4fa1a1SMauro Carvalho Chehab  *
179d4fa1a1SMauro Carvalho Chehab  */
189d4fa1a1SMauro Carvalho Chehab #include <linux/module.h>
199d4fa1a1SMauro Carvalho Chehab #include <linux/uaccess.h>
209d4fa1a1SMauro Carvalho Chehab #include <linux/delay.h>
219d4fa1a1SMauro Carvalho Chehab #include <linux/device.h>
229d4fa1a1SMauro Carvalho Chehab #include <linux/mm.h>
239d4fa1a1SMauro Carvalho Chehab #include <linux/sched.h>
249d4fa1a1SMauro Carvalho Chehab #include <linux/slab.h>
259d4fa1a1SMauro Carvalho Chehab 
269d4fa1a1SMauro Carvalho Chehab #include <media/v4l2-event.h>
279d4fa1a1SMauro Carvalho Chehab #include <media/v4l2-mediabus.h>
2820734fcaSHans de Goede #include <media/videobuf2-vmalloc.h>
299d4fa1a1SMauro Carvalho Chehab #include "atomisp_cmd.h"
309d4fa1a1SMauro Carvalho Chehab #include "atomisp_common.h"
319d4fa1a1SMauro Carvalho Chehab #include "atomisp_compat.h"
3220734fcaSHans de Goede #include "atomisp_fops.h"
339d4fa1a1SMauro Carvalho Chehab #include "atomisp_internal.h"
349d4fa1a1SMauro Carvalho Chehab 
359d4fa1a1SMauro Carvalho Chehab const struct atomisp_in_fmt_conv atomisp_in_fmt_conv[] = {
36fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, ATOMISP_INPUT_FORMAT_RAW_8, IA_CSS_BAYER_ORDER_BGGR },
37fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, ATOMISP_INPUT_FORMAT_RAW_8, IA_CSS_BAYER_ORDER_GBRG },
38fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, ATOMISP_INPUT_FORMAT_RAW_8, IA_CSS_BAYER_ORDER_GRBG },
39fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, ATOMISP_INPUT_FORMAT_RAW_8, IA_CSS_BAYER_ORDER_RGGB },
40fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, ATOMISP_INPUT_FORMAT_RAW_10, IA_CSS_BAYER_ORDER_BGGR },
41fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, ATOMISP_INPUT_FORMAT_RAW_10, IA_CSS_BAYER_ORDER_GBRG },
42fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, ATOMISP_INPUT_FORMAT_RAW_10, IA_CSS_BAYER_ORDER_GRBG },
43fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, ATOMISP_INPUT_FORMAT_RAW_10, IA_CSS_BAYER_ORDER_RGGB },
44fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, IA_CSS_BAYER_ORDER_BGGR },
45fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, IA_CSS_BAYER_ORDER_GBRG },
46fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, IA_CSS_BAYER_ORDER_GRBG },
47fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, IA_CSS_BAYER_ORDER_RGGB },
48fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0 },
49fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0 },
50065e5e55SArnd Bergmann #if 0 // disabled due to clang warnings
51fb1f6ae6SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_JPEG_1X8, 8, 8, IA_CSS_FRAME_FORMAT_BINARY_8, 0 },
52fb1f6ae6SMauro Carvalho Chehab 	{ V4L2_MBUS_FMT_CUSTOM_NV12, 12, 12, IA_CSS_FRAME_FORMAT_NV12, 0 },
53fb1f6ae6SMauro Carvalho Chehab 	{ V4L2_MBUS_FMT_CUSTOM_NV21, 12, 12, IA_CSS_FRAME_FORMAT_NV21, 0 },
54065e5e55SArnd Bergmann #endif
55fb1f6ae6SMauro Carvalho Chehab 	{ V4L2_MBUS_FMT_CUSTOM_YUV420, 12, 12, ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY, 0 },
569d4fa1a1SMauro Carvalho Chehab #if 0
57fb1f6ae6SMauro Carvalho Chehab 	{ V4L2_MBUS_FMT_CUSTOM_M10MO_RAW, 8, 8, IA_CSS_FRAME_FORMAT_BINARY_8, 0 },
589d4fa1a1SMauro Carvalho Chehab #endif
599d4fa1a1SMauro Carvalho Chehab 	/* no valid V4L2 MBUS code for metadata format, so leave it 0. */
60fb1f6ae6SMauro Carvalho Chehab 	{ 0, 0, 0, ATOMISP_INPUT_FORMAT_EMBEDDED, 0 },
619d4fa1a1SMauro Carvalho Chehab 	{}
629d4fa1a1SMauro Carvalho Chehab };
639d4fa1a1SMauro Carvalho Chehab 
649d4fa1a1SMauro Carvalho Chehab static const struct {
659d4fa1a1SMauro Carvalho Chehab 	u32 code;
669d4fa1a1SMauro Carvalho Chehab 	u32 compressed;
679d4fa1a1SMauro Carvalho Chehab } compressed_codes[] = {
689d4fa1a1SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 },
699d4fa1a1SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 },
709d4fa1a1SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 },
719d4fa1a1SMauro Carvalho Chehab 	{ MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 },
729d4fa1a1SMauro Carvalho Chehab };
739d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_uncompressed_code(u32 code)749d4fa1a1SMauro Carvalho Chehab u32 atomisp_subdev_uncompressed_code(u32 code)
759d4fa1a1SMauro Carvalho Chehab {
769d4fa1a1SMauro Carvalho Chehab 	unsigned int i;
779d4fa1a1SMauro Carvalho Chehab 
789d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(compressed_codes); i++)
799d4fa1a1SMauro Carvalho Chehab 		if (code == compressed_codes[i].compressed)
809d4fa1a1SMauro Carvalho Chehab 			return compressed_codes[i].code;
819d4fa1a1SMauro Carvalho Chehab 
829d4fa1a1SMauro Carvalho Chehab 	return code;
839d4fa1a1SMauro Carvalho Chehab }
849d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_is_compressed(u32 code)859d4fa1a1SMauro Carvalho Chehab bool atomisp_subdev_is_compressed(u32 code)
869d4fa1a1SMauro Carvalho Chehab {
879d4fa1a1SMauro Carvalho Chehab 	int i;
889d4fa1a1SMauro Carvalho Chehab 
899d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(atomisp_in_fmt_conv) - 1; i++)
909d4fa1a1SMauro Carvalho Chehab 		if (code == atomisp_in_fmt_conv[i].code)
919d4fa1a1SMauro Carvalho Chehab 			return atomisp_in_fmt_conv[i].bpp !=
929d4fa1a1SMauro Carvalho Chehab 			       atomisp_in_fmt_conv[i].depth;
939d4fa1a1SMauro Carvalho Chehab 
949d4fa1a1SMauro Carvalho Chehab 	return false;
959d4fa1a1SMauro Carvalho Chehab }
969d4fa1a1SMauro Carvalho Chehab 
atomisp_find_in_fmt_conv(u32 code)979d4fa1a1SMauro Carvalho Chehab const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv(u32 code)
989d4fa1a1SMauro Carvalho Chehab {
999d4fa1a1SMauro Carvalho Chehab 	int i;
1009d4fa1a1SMauro Carvalho Chehab 
1019d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(atomisp_in_fmt_conv) - 1; i++)
1029d4fa1a1SMauro Carvalho Chehab 		if (code == atomisp_in_fmt_conv[i].code)
1039d4fa1a1SMauro Carvalho Chehab 			return atomisp_in_fmt_conv + i;
1049d4fa1a1SMauro Carvalho Chehab 
1059d4fa1a1SMauro Carvalho Chehab 	return NULL;
1069d4fa1a1SMauro Carvalho Chehab }
1079d4fa1a1SMauro Carvalho Chehab 
atomisp_find_in_fmt_conv_by_atomisp_in_fmt(enum atomisp_input_format atomisp_in_fmt)1089d4fa1a1SMauro Carvalho Chehab const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_by_atomisp_in_fmt(
1099d4fa1a1SMauro Carvalho Chehab     enum atomisp_input_format atomisp_in_fmt)
1109d4fa1a1SMauro Carvalho Chehab {
1119d4fa1a1SMauro Carvalho Chehab 	int i;
1129d4fa1a1SMauro Carvalho Chehab 
1139d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(atomisp_in_fmt_conv) - 1; i++)
1149d4fa1a1SMauro Carvalho Chehab 		if (atomisp_in_fmt_conv[i].atomisp_in_fmt == atomisp_in_fmt)
1159d4fa1a1SMauro Carvalho Chehab 			return atomisp_in_fmt_conv + i;
1169d4fa1a1SMauro Carvalho Chehab 
1179d4fa1a1SMauro Carvalho Chehab 	return NULL;
1189d4fa1a1SMauro Carvalho Chehab }
1199d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_format_conversion(struct atomisp_sub_device * asd)120e4454e06SHans de Goede bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd)
1219d4fa1a1SMauro Carvalho Chehab {
1229d4fa1a1SMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *sink, *src;
1239d4fa1a1SMauro Carvalho Chehab 
1249d4fa1a1SMauro Carvalho Chehab 	sink = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
125e4454e06SHans de Goede 				       V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK);
1269d4fa1a1SMauro Carvalho Chehab 	src = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
127e4454e06SHans de Goede 				      V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SOURCE);
1289d4fa1a1SMauro Carvalho Chehab 
1299d4fa1a1SMauro Carvalho Chehab 	return atomisp_is_mbuscode_raw(sink->code)
1309d4fa1a1SMauro Carvalho Chehab 	       && !atomisp_is_mbuscode_raw(src->code);
1319d4fa1a1SMauro Carvalho Chehab }
1329d4fa1a1SMauro Carvalho Chehab 
1339d4fa1a1SMauro Carvalho Chehab /*
1349d4fa1a1SMauro Carvalho Chehab  * V4L2 subdev operations
1359d4fa1a1SMauro Carvalho Chehab  */
1369d4fa1a1SMauro Carvalho Chehab 
1379d4fa1a1SMauro Carvalho Chehab /*
1389d4fa1a1SMauro Carvalho Chehab  * isp_subdev_ioctl - CCDC module private ioctl's
1399d4fa1a1SMauro Carvalho Chehab  * @sd: ISP V4L2 subdevice
1409d4fa1a1SMauro Carvalho Chehab  * @cmd: ioctl command
1419d4fa1a1SMauro Carvalho Chehab  * @arg: ioctl argument
1429d4fa1a1SMauro Carvalho Chehab  *
1439d4fa1a1SMauro Carvalho Chehab  * Return 0 on success or a negative error code otherwise.
1449d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_ioctl(struct v4l2_subdev * sd,unsigned int cmd,void * arg)1459d4fa1a1SMauro Carvalho Chehab static long isp_subdev_ioctl(struct v4l2_subdev *sd,
1469d4fa1a1SMauro Carvalho Chehab 			     unsigned int cmd, void *arg)
1479d4fa1a1SMauro Carvalho Chehab {
1489d4fa1a1SMauro Carvalho Chehab 	return 0;
1499d4fa1a1SMauro Carvalho Chehab }
1509d4fa1a1SMauro Carvalho Chehab 
1519d4fa1a1SMauro Carvalho Chehab /*
1529d4fa1a1SMauro Carvalho Chehab  * isp_subdev_set_power - Power on/off the CCDC module
1539d4fa1a1SMauro Carvalho Chehab  * @sd: ISP V4L2 subdevice
1549d4fa1a1SMauro Carvalho Chehab  * @on: power on/off
1559d4fa1a1SMauro Carvalho Chehab  *
1569d4fa1a1SMauro Carvalho Chehab  * Return 0 on success or a negative error code otherwise.
1579d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_set_power(struct v4l2_subdev * sd,int on)1589d4fa1a1SMauro Carvalho Chehab static int isp_subdev_set_power(struct v4l2_subdev *sd, int on)
1599d4fa1a1SMauro Carvalho Chehab {
1609d4fa1a1SMauro Carvalho Chehab 	return 0;
1619d4fa1a1SMauro Carvalho Chehab }
1629d4fa1a1SMauro Carvalho Chehab 
isp_subdev_subscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)1639d4fa1a1SMauro Carvalho Chehab static int isp_subdev_subscribe_event(struct v4l2_subdev *sd,
1649d4fa1a1SMauro Carvalho Chehab 				      struct v4l2_fh *fh,
1659d4fa1a1SMauro Carvalho Chehab 				      struct v4l2_event_subscription *sub)
1669d4fa1a1SMauro Carvalho Chehab {
1679d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
1689d4fa1a1SMauro Carvalho Chehab 	struct atomisp_device *isp = isp_sd->isp;
1699d4fa1a1SMauro Carvalho Chehab 
1709d4fa1a1SMauro Carvalho Chehab 	if (sub->type != V4L2_EVENT_FRAME_SYNC &&
1719d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_FRAME_END &&
1729d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_ATOMISP_3A_STATS_READY &&
1739d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_ATOMISP_METADATA_READY &&
1749d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_ATOMISP_PAUSE_BUFFER &&
1759d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_ATOMISP_CSS_RESET &&
1769d4fa1a1SMauro Carvalho Chehab 	    sub->type != V4L2_EVENT_ATOMISP_ACC_COMPLETE)
1779d4fa1a1SMauro Carvalho Chehab 		return -EINVAL;
1789d4fa1a1SMauro Carvalho Chehab 
1799d4fa1a1SMauro Carvalho Chehab 	if (sub->type == V4L2_EVENT_FRAME_SYNC &&
1809d4fa1a1SMauro Carvalho Chehab 	    !atomisp_css_valid_sof(isp))
1819d4fa1a1SMauro Carvalho Chehab 		return -EINVAL;
1829d4fa1a1SMauro Carvalho Chehab 
1839d4fa1a1SMauro Carvalho Chehab 	return v4l2_event_subscribe(fh, sub, 16, NULL);
1849d4fa1a1SMauro Carvalho Chehab }
1859d4fa1a1SMauro Carvalho Chehab 
isp_subdev_unsubscribe_event(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)1869d4fa1a1SMauro Carvalho Chehab static int isp_subdev_unsubscribe_event(struct v4l2_subdev *sd,
1879d4fa1a1SMauro Carvalho Chehab 					struct v4l2_fh *fh,
1889d4fa1a1SMauro Carvalho Chehab 					struct v4l2_event_subscription *sub)
1899d4fa1a1SMauro Carvalho Chehab {
1909d4fa1a1SMauro Carvalho Chehab 	return v4l2_event_unsubscribe(fh, sub);
1919d4fa1a1SMauro Carvalho Chehab }
1929d4fa1a1SMauro Carvalho Chehab 
1939d4fa1a1SMauro Carvalho Chehab /*
1949d4fa1a1SMauro Carvalho Chehab  * isp_subdev_enum_mbus_code - Handle pixel format enumeration
1959d4fa1a1SMauro Carvalho Chehab  * @sd: pointer to v4l2 subdev structure
1969d4fa1a1SMauro Carvalho Chehab  * @fh : V4L2 subdev file handle
1979d4fa1a1SMauro Carvalho Chehab  * @code: pointer to v4l2_subdev_pad_mbus_code_enum structure
1989d4fa1a1SMauro Carvalho Chehab  * return -EINVAL or zero on success
1999d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)2009d4fa1a1SMauro Carvalho Chehab static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd,
2010d346d2aSTomi Valkeinen 				     struct v4l2_subdev_state *sd_state,
2029d4fa1a1SMauro Carvalho Chehab 				     struct v4l2_subdev_mbus_code_enum *code)
2039d4fa1a1SMauro Carvalho Chehab {
2049d4fa1a1SMauro Carvalho Chehab 	if (code->index >= ARRAY_SIZE(atomisp_in_fmt_conv) - 1)
2059d4fa1a1SMauro Carvalho Chehab 		return -EINVAL;
2069d4fa1a1SMauro Carvalho Chehab 
2079d4fa1a1SMauro Carvalho Chehab 	code->code = atomisp_in_fmt_conv[code->index].code;
2089d4fa1a1SMauro Carvalho Chehab 
2099d4fa1a1SMauro Carvalho Chehab 	return 0;
2109d4fa1a1SMauro Carvalho Chehab }
2119d4fa1a1SMauro Carvalho Chehab 
isp_subdev_validate_rect(struct v4l2_subdev * sd,uint32_t pad,uint32_t target)2129d4fa1a1SMauro Carvalho Chehab static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad,
2139d4fa1a1SMauro Carvalho Chehab 				    uint32_t target)
2149d4fa1a1SMauro Carvalho Chehab {
2159d4fa1a1SMauro Carvalho Chehab 	switch (pad) {
2169d4fa1a1SMauro Carvalho Chehab 	case ATOMISP_SUBDEV_PAD_SINK:
2179d4fa1a1SMauro Carvalho Chehab 		switch (target) {
2189d4fa1a1SMauro Carvalho Chehab 		case V4L2_SEL_TGT_CROP:
2199d4fa1a1SMauro Carvalho Chehab 			return 0;
2209d4fa1a1SMauro Carvalho Chehab 		}
2219d4fa1a1SMauro Carvalho Chehab 		break;
2229d4fa1a1SMauro Carvalho Chehab 	default:
2239d4fa1a1SMauro Carvalho Chehab 		switch (target) {
2249d4fa1a1SMauro Carvalho Chehab 		case V4L2_SEL_TGT_COMPOSE:
2259d4fa1a1SMauro Carvalho Chehab 			return 0;
2269d4fa1a1SMauro Carvalho Chehab 		}
2279d4fa1a1SMauro Carvalho Chehab 		break;
2289d4fa1a1SMauro Carvalho Chehab 	}
2299d4fa1a1SMauro Carvalho Chehab 
2309d4fa1a1SMauro Carvalho Chehab 	return -EINVAL;
2319d4fa1a1SMauro Carvalho Chehab }
2329d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_get_rect(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,u32 which,uint32_t pad,uint32_t target)2339d4fa1a1SMauro Carvalho Chehab struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd,
2340d346d2aSTomi Valkeinen 	struct v4l2_subdev_state *sd_state,
2359d4fa1a1SMauro Carvalho Chehab 	u32 which, uint32_t pad,
2369d4fa1a1SMauro Carvalho Chehab 	uint32_t target)
2379d4fa1a1SMauro Carvalho Chehab {
2389d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
2399d4fa1a1SMauro Carvalho Chehab 
2409d4fa1a1SMauro Carvalho Chehab 	if (which == V4L2_SUBDEV_FORMAT_TRY) {
2419d4fa1a1SMauro Carvalho Chehab 		switch (target) {
2429d4fa1a1SMauro Carvalho Chehab 		case V4L2_SEL_TGT_CROP:
2430d346d2aSTomi Valkeinen 			return v4l2_subdev_get_try_crop(sd, sd_state, pad);
2449d4fa1a1SMauro Carvalho Chehab 		case V4L2_SEL_TGT_COMPOSE:
2450d346d2aSTomi Valkeinen 			return v4l2_subdev_get_try_compose(sd, sd_state, pad);
2469d4fa1a1SMauro Carvalho Chehab 		}
2479d4fa1a1SMauro Carvalho Chehab 	}
2489d4fa1a1SMauro Carvalho Chehab 
2499d4fa1a1SMauro Carvalho Chehab 	switch (target) {
2509d4fa1a1SMauro Carvalho Chehab 	case V4L2_SEL_TGT_CROP:
2519d4fa1a1SMauro Carvalho Chehab 		return &isp_sd->fmt[pad].crop;
2529d4fa1a1SMauro Carvalho Chehab 	case V4L2_SEL_TGT_COMPOSE:
2539d4fa1a1SMauro Carvalho Chehab 		return &isp_sd->fmt[pad].compose;
2549d4fa1a1SMauro Carvalho Chehab 	}
2559d4fa1a1SMauro Carvalho Chehab 
2569d4fa1a1SMauro Carvalho Chehab 	return NULL;
2579d4fa1a1SMauro Carvalho Chehab }
2589d4fa1a1SMauro Carvalho Chehab 
2599d4fa1a1SMauro Carvalho Chehab struct v4l2_mbus_framefmt
atomisp_subdev_get_ffmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,uint32_t which,uint32_t pad)2609d4fa1a1SMauro Carvalho Chehab *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd,
2610d346d2aSTomi Valkeinen 			 struct v4l2_subdev_state *sd_state, uint32_t which,
2629d4fa1a1SMauro Carvalho Chehab 			 uint32_t pad)
2639d4fa1a1SMauro Carvalho Chehab {
2649d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
2659d4fa1a1SMauro Carvalho Chehab 
2669d4fa1a1SMauro Carvalho Chehab 	if (which == V4L2_SUBDEV_FORMAT_TRY)
2670d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(sd, sd_state, pad);
2689d4fa1a1SMauro Carvalho Chehab 
2699d4fa1a1SMauro Carvalho Chehab 	return &isp_sd->fmt[pad].fmt;
2709d4fa1a1SMauro Carvalho Chehab }
2719d4fa1a1SMauro Carvalho Chehab 
isp_get_fmt_rect(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,uint32_t which,struct v4l2_mbus_framefmt ** ffmt,struct v4l2_rect * crop[ATOMISP_SUBDEV_PADS_NUM],struct v4l2_rect * comp[ATOMISP_SUBDEV_PADS_NUM])2729d4fa1a1SMauro Carvalho Chehab static void isp_get_fmt_rect(struct v4l2_subdev *sd,
2730d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
2740d346d2aSTomi Valkeinen 			     uint32_t which,
2759d4fa1a1SMauro Carvalho Chehab 			     struct v4l2_mbus_framefmt **ffmt,
2769d4fa1a1SMauro Carvalho Chehab 			     struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM],
2779d4fa1a1SMauro Carvalho Chehab 			     struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM])
2789d4fa1a1SMauro Carvalho Chehab {
2799d4fa1a1SMauro Carvalho Chehab 	unsigned int i;
2809d4fa1a1SMauro Carvalho Chehab 
2819d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ATOMISP_SUBDEV_PADS_NUM; i++) {
2820d346d2aSTomi Valkeinen 		ffmt[i] = atomisp_subdev_get_ffmt(sd, sd_state, which, i);
2830d346d2aSTomi Valkeinen 		crop[i] = atomisp_subdev_get_rect(sd, sd_state, which, i,
2849d4fa1a1SMauro Carvalho Chehab 						  V4L2_SEL_TGT_CROP);
2850d346d2aSTomi Valkeinen 		comp[i] = atomisp_subdev_get_rect(sd, sd_state, which, i,
2869d4fa1a1SMauro Carvalho Chehab 						  V4L2_SEL_TGT_COMPOSE);
2879d4fa1a1SMauro Carvalho Chehab 	}
2889d4fa1a1SMauro Carvalho Chehab }
2899d4fa1a1SMauro Carvalho Chehab 
isp_subdev_propagate(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,u32 which,uint32_t pad,uint32_t target,uint32_t flags)2909d4fa1a1SMauro Carvalho Chehab static void isp_subdev_propagate(struct v4l2_subdev *sd,
2910d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
2929d4fa1a1SMauro Carvalho Chehab 				 u32 which, uint32_t pad, uint32_t target,
2939d4fa1a1SMauro Carvalho Chehab 				 uint32_t flags)
2949d4fa1a1SMauro Carvalho Chehab {
2959d4fa1a1SMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *ffmt[ATOMISP_SUBDEV_PADS_NUM];
2969d4fa1a1SMauro Carvalho Chehab 	struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM],
2979d4fa1a1SMauro Carvalho Chehab 		       *comp[ATOMISP_SUBDEV_PADS_NUM];
2989d4fa1a1SMauro Carvalho Chehab 
2999d4fa1a1SMauro Carvalho Chehab 	if (flags & V4L2_SEL_FLAG_KEEP_CONFIG)
3009d4fa1a1SMauro Carvalho Chehab 		return;
3019d4fa1a1SMauro Carvalho Chehab 
3020d346d2aSTomi Valkeinen 	isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp);
3039d4fa1a1SMauro Carvalho Chehab 
3049d4fa1a1SMauro Carvalho Chehab 	switch (pad) {
3059d4fa1a1SMauro Carvalho Chehab 	case ATOMISP_SUBDEV_PAD_SINK: {
3069d4fa1a1SMauro Carvalho Chehab 		struct v4l2_rect r = {0};
3079d4fa1a1SMauro Carvalho Chehab 
3089d4fa1a1SMauro Carvalho Chehab 		/* Only crop target supported on sink pad. */
3099d4fa1a1SMauro Carvalho Chehab 		r.width = ffmt[pad]->width;
3109d4fa1a1SMauro Carvalho Chehab 		r.height = ffmt[pad]->height;
3119d4fa1a1SMauro Carvalho Chehab 
3120d346d2aSTomi Valkeinen 		atomisp_subdev_set_selection(sd, sd_state, which, pad,
3139d4fa1a1SMauro Carvalho Chehab 					     target, flags, &r);
3149d4fa1a1SMauro Carvalho Chehab 		break;
3159d4fa1a1SMauro Carvalho Chehab 	}
3169d4fa1a1SMauro Carvalho Chehab 	}
3179d4fa1a1SMauro Carvalho Chehab }
3189d4fa1a1SMauro Carvalho Chehab 
isp_subdev_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)3199d4fa1a1SMauro Carvalho Chehab static int isp_subdev_get_selection(struct v4l2_subdev *sd,
3200d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
3219d4fa1a1SMauro Carvalho Chehab 				    struct v4l2_subdev_selection *sel)
3229d4fa1a1SMauro Carvalho Chehab {
3239d4fa1a1SMauro Carvalho Chehab 	struct v4l2_rect *rec;
3249d4fa1a1SMauro Carvalho Chehab 	int rval = isp_subdev_validate_rect(sd, sel->pad, sel->target);
3259d4fa1a1SMauro Carvalho Chehab 
3269d4fa1a1SMauro Carvalho Chehab 	if (rval)
3279d4fa1a1SMauro Carvalho Chehab 		return rval;
3289d4fa1a1SMauro Carvalho Chehab 
3290d346d2aSTomi Valkeinen 	rec = atomisp_subdev_get_rect(sd, sd_state, sel->which, sel->pad,
3309d4fa1a1SMauro Carvalho Chehab 				      sel->target);
3319d4fa1a1SMauro Carvalho Chehab 	if (!rec)
3329d4fa1a1SMauro Carvalho Chehab 		return -EINVAL;
3339d4fa1a1SMauro Carvalho Chehab 
3349d4fa1a1SMauro Carvalho Chehab 	sel->r = *rec;
3359d4fa1a1SMauro Carvalho Chehab 	return 0;
3369d4fa1a1SMauro Carvalho Chehab }
3379d4fa1a1SMauro Carvalho Chehab 
atomisp_pad_str(unsigned int pad)33862599316SDan Carpenter static const char *atomisp_pad_str(unsigned int pad)
33962599316SDan Carpenter {
34062599316SDan Carpenter 	static const char *const pad_str[] = {
34162599316SDan Carpenter 		"ATOMISP_SUBDEV_PAD_SINK",
342aca8a1dfSHans de Goede 		"ATOMISP_SUBDEV_PAD_SOURCE",
3439d4fa1a1SMauro Carvalho Chehab 	};
3449d4fa1a1SMauro Carvalho Chehab 
34562599316SDan Carpenter 	if (pad >= ARRAY_SIZE(pad_str))
34662599316SDan Carpenter 		return "ATOMISP_INVALID_PAD";
34762599316SDan Carpenter 	return pad_str[pad];
34862599316SDan Carpenter }
34962599316SDan Carpenter 
atomisp_subdev_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,u32 which,uint32_t pad,uint32_t target,u32 flags,struct v4l2_rect * r)3509d4fa1a1SMauro Carvalho Chehab int atomisp_subdev_set_selection(struct v4l2_subdev *sd,
3510d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
3529d4fa1a1SMauro Carvalho Chehab 				 u32 which, uint32_t pad, uint32_t target,
3539d4fa1a1SMauro Carvalho Chehab 				 u32 flags, struct v4l2_rect *r)
3549d4fa1a1SMauro Carvalho Chehab {
3559d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
3569d4fa1a1SMauro Carvalho Chehab 	struct atomisp_device *isp = isp_sd->isp;
3579d4fa1a1SMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *ffmt[ATOMISP_SUBDEV_PADS_NUM];
3589d4fa1a1SMauro Carvalho Chehab 	struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM],
3599d4fa1a1SMauro Carvalho Chehab 		       *comp[ATOMISP_SUBDEV_PADS_NUM];
3609d4fa1a1SMauro Carvalho Chehab 
361566f6de6SHans de Goede 	if ((pad == ATOMISP_SUBDEV_PAD_SINK && target != V4L2_SEL_TGT_CROP) ||
362566f6de6SHans de Goede 	    (pad == ATOMISP_SUBDEV_PAD_SOURCE && target != V4L2_SEL_TGT_COMPOSE))
363566f6de6SHans de Goede 		return -EINVAL;
364566f6de6SHans de Goede 
3650d346d2aSTomi Valkeinen 	isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp);
3669d4fa1a1SMauro Carvalho Chehab 
3679d4fa1a1SMauro Carvalho Chehab 	dev_dbg(isp->dev,
3689d4fa1a1SMauro Carvalho Chehab 		"sel: pad %s tgt %s l %d t %d w %d h %d which %s f 0x%8.8x\n",
36962599316SDan Carpenter 		atomisp_pad_str(pad), target == V4L2_SEL_TGT_CROP
3709d4fa1a1SMauro Carvalho Chehab 		? "V4L2_SEL_TGT_CROP" : "V4L2_SEL_TGT_COMPOSE",
3719d4fa1a1SMauro Carvalho Chehab 		r->left, r->top, r->width, r->height,
3729d4fa1a1SMauro Carvalho Chehab 		which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY"
3739d4fa1a1SMauro Carvalho Chehab 		: "V4L2_SUBDEV_FORMAT_ACTIVE", flags);
3749d4fa1a1SMauro Carvalho Chehab 
3759d4fa1a1SMauro Carvalho Chehab 	r->width = rounddown(r->width, ATOM_ISP_STEP_WIDTH);
3769d4fa1a1SMauro Carvalho Chehab 	r->height = rounddown(r->height, ATOM_ISP_STEP_HEIGHT);
3779d4fa1a1SMauro Carvalho Chehab 
378ea360037SHans de Goede 	if (pad == ATOMISP_SUBDEV_PAD_SINK) {
3799d4fa1a1SMauro Carvalho Chehab 		/* Only crop target supported on sink pad. */
3809d4fa1a1SMauro Carvalho Chehab 		unsigned int dvs_w, dvs_h;
3819d4fa1a1SMauro Carvalho Chehab 
3829d4fa1a1SMauro Carvalho Chehab 		crop[pad]->width = ffmt[pad]->width;
3839d4fa1a1SMauro Carvalho Chehab 		crop[pad]->height = ffmt[pad]->height;
3849d4fa1a1SMauro Carvalho Chehab 
385e4454e06SHans de Goede 		if (atomisp_subdev_format_conversion(isp_sd)
38601049493SJoe Perches 		    && crop[pad]->width && crop[pad]->height) {
387*3e399cf2SHans de Goede 			crop[pad]->width -= isp_sd->sink_pad_padding_w;
388*3e399cf2SHans de Goede 			crop[pad]->height -= isp_sd->sink_pad_padding_h;
38901049493SJoe Perches 		}
3909d4fa1a1SMauro Carvalho Chehab 
3919d4fa1a1SMauro Carvalho Chehab 		if (isp_sd->params.video_dis_en &&
3926048fc1cSHans de Goede 		    isp_sd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
3939d4fa1a1SMauro Carvalho Chehab 			/* This resolution contains 20 % of DVS slack
3949d4fa1a1SMauro Carvalho Chehab 			 * (of the desired captured image before
3959d4fa1a1SMauro Carvalho Chehab 			 * scaling, or 1 / 6 of what we get from the
3969d4fa1a1SMauro Carvalho Chehab 			 * sensor) in both width and height. Remove
3979d4fa1a1SMauro Carvalho Chehab 			 * it. */
3989d4fa1a1SMauro Carvalho Chehab 			crop[pad]->width = roundup(crop[pad]->width * 5 / 6,
3999d4fa1a1SMauro Carvalho Chehab 						   ATOM_ISP_STEP_WIDTH);
4009d4fa1a1SMauro Carvalho Chehab 			crop[pad]->height = roundup(crop[pad]->height * 5 / 6,
4019d4fa1a1SMauro Carvalho Chehab 						    ATOM_ISP_STEP_HEIGHT);
4029d4fa1a1SMauro Carvalho Chehab 		}
4039d4fa1a1SMauro Carvalho Chehab 
4049d4fa1a1SMauro Carvalho Chehab 		crop[pad]->width = min(crop[pad]->width, r->width);
4059d4fa1a1SMauro Carvalho Chehab 		crop[pad]->height = min(crop[pad]->height, r->height);
4069d4fa1a1SMauro Carvalho Chehab 
4079d4fa1a1SMauro Carvalho Chehab 		if (!(flags & V4L2_SEL_FLAG_KEEP_CONFIG)) {
4089d4fa1a1SMauro Carvalho Chehab 			struct v4l2_rect tmp = *crop[pad];
4099d4fa1a1SMauro Carvalho Chehab 
410d957824bSHans de Goede 			atomisp_subdev_set_selection(sd, sd_state, which,
411aca8a1dfSHans de Goede 						     ATOMISP_SUBDEV_PAD_SOURCE,
412d957824bSHans de Goede 						     V4L2_SEL_TGT_COMPOSE, flags, &tmp);
4139d4fa1a1SMauro Carvalho Chehab 		}
4149d4fa1a1SMauro Carvalho Chehab 
4159d4fa1a1SMauro Carvalho Chehab 		if (which == V4L2_SUBDEV_FORMAT_TRY)
416ea360037SHans de Goede 			goto get_rect;
4179d4fa1a1SMauro Carvalho Chehab 
4189d4fa1a1SMauro Carvalho Chehab 		if (isp_sd->params.video_dis_en &&
4196048fc1cSHans de Goede 		    isp_sd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
4209d4fa1a1SMauro Carvalho Chehab 			dvs_w = rounddown(crop[pad]->width / 5,
4219d4fa1a1SMauro Carvalho Chehab 					  ATOM_ISP_STEP_WIDTH);
4229d4fa1a1SMauro Carvalho Chehab 			dvs_h = rounddown(crop[pad]->height / 5,
4239d4fa1a1SMauro Carvalho Chehab 					  ATOM_ISP_STEP_HEIGHT);
4249d4fa1a1SMauro Carvalho Chehab 		} else if (!isp_sd->params.video_dis_en &&
4259d4fa1a1SMauro Carvalho Chehab 			   isp_sd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
4269d4fa1a1SMauro Carvalho Chehab 			/*
4279d4fa1a1SMauro Carvalho Chehab 			 * For CSS2.0, digital zoom needs to set dvs envelope to 12
4289d4fa1a1SMauro Carvalho Chehab 			 * when dvs is disabled.
4299d4fa1a1SMauro Carvalho Chehab 			 */
4309d4fa1a1SMauro Carvalho Chehab 			dvs_w = dvs_h = 12;
43141d1f1b0SAline Santana Cordeiro 		} else {
4329d4fa1a1SMauro Carvalho Chehab 			dvs_w = dvs_h = 0;
43341d1f1b0SAline Santana Cordeiro 		}
4349d4fa1a1SMauro Carvalho Chehab 		atomisp_css_video_set_dis_envelope(isp_sd, dvs_w, dvs_h);
4351e32f6eaSHans de Goede 		atomisp_css_input_set_effective_resolution(isp_sd,
4361e32f6eaSHans de Goede 							   ATOMISP_INPUT_STREAM_GENERAL,
4371e32f6eaSHans de Goede 							   crop[pad]->width,
4381e32f6eaSHans de Goede 							   crop[pad]->height);
439ea360037SHans de Goede 	} else if (isp_sd->run_mode->val != ATOMISP_RUN_MODE_PREVIEW) {
4409d4fa1a1SMauro Carvalho Chehab 		/* Only compose target is supported on source pads. */
4419d4fa1a1SMauro Carvalho Chehab 		if (isp_sd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) {
4429d4fa1a1SMauro Carvalho Chehab 			/* Scaling is disabled in this mode */
4439d4fa1a1SMauro Carvalho Chehab 			r->width = crop[ATOMISP_SUBDEV_PAD_SINK]->width;
4449d4fa1a1SMauro Carvalho Chehab 			r->height = crop[ATOMISP_SUBDEV_PAD_SINK]->height;
4459d4fa1a1SMauro Carvalho Chehab 		}
4469d4fa1a1SMauro Carvalho Chehab 
4479d4fa1a1SMauro Carvalho Chehab 		if (crop[ATOMISP_SUBDEV_PAD_SINK]->width == r->width
4489d4fa1a1SMauro Carvalho Chehab 		    && crop[ATOMISP_SUBDEV_PAD_SINK]->height == r->height)
4499d4fa1a1SMauro Carvalho Chehab 			isp_sd->params.yuv_ds_en = false;
4509d4fa1a1SMauro Carvalho Chehab 		else
4519d4fa1a1SMauro Carvalho Chehab 			isp_sd->params.yuv_ds_en = true;
4529d4fa1a1SMauro Carvalho Chehab 
4539d4fa1a1SMauro Carvalho Chehab 		comp[pad]->width = r->width;
4549d4fa1a1SMauro Carvalho Chehab 		comp[pad]->height = r->height;
4559d4fa1a1SMauro Carvalho Chehab 
4569d4fa1a1SMauro Carvalho Chehab 		if (r->width == 0 || r->height == 0 ||
4579d4fa1a1SMauro Carvalho Chehab 		    crop[ATOMISP_SUBDEV_PAD_SINK]->width == 0 ||
4589d4fa1a1SMauro Carvalho Chehab 		    crop[ATOMISP_SUBDEV_PAD_SINK]->height == 0)
459ea360037SHans de Goede 			goto get_rect;
4609d4fa1a1SMauro Carvalho Chehab 		/*
4619d4fa1a1SMauro Carvalho Chehab 		 * do cropping on sensor input if ratio of required resolution
4629d4fa1a1SMauro Carvalho Chehab 		 * is different with sensor output resolution ratio:
4639d4fa1a1SMauro Carvalho Chehab 		 *
4649d4fa1a1SMauro Carvalho Chehab 		 * ratio = width / height
4659d4fa1a1SMauro Carvalho Chehab 		 *
4669d4fa1a1SMauro Carvalho Chehab 		 * if ratio_output < ratio_sensor:
4679d4fa1a1SMauro Carvalho Chehab 		 *	effect_width = sensor_height * out_width / out_height;
4689d4fa1a1SMauro Carvalho Chehab 		 *	effect_height = sensor_height;
4699d4fa1a1SMauro Carvalho Chehab 		 * else
4709d4fa1a1SMauro Carvalho Chehab 		 *	effect_width = sensor_width;
4719d4fa1a1SMauro Carvalho Chehab 		 *	effect_height = sensor_width * out_height / out_width;
4729d4fa1a1SMauro Carvalho Chehab 		 *
4739d4fa1a1SMauro Carvalho Chehab 		 */
4749d4fa1a1SMauro Carvalho Chehab 		if (r->width * crop[ATOMISP_SUBDEV_PAD_SINK]->height <
4759d4fa1a1SMauro Carvalho Chehab 		    crop[ATOMISP_SUBDEV_PAD_SINK]->width * r->height)
4769d4fa1a1SMauro Carvalho Chehab 			atomisp_css_input_set_effective_resolution(isp_sd,
4771e32f6eaSHans de Goede 				ATOMISP_INPUT_STREAM_GENERAL,
4789d4fa1a1SMauro Carvalho Chehab 				rounddown(crop[ATOMISP_SUBDEV_PAD_SINK]->
4799d4fa1a1SMauro Carvalho Chehab 					  height * r->width / r->height,
4809d4fa1a1SMauro Carvalho Chehab 					  ATOM_ISP_STEP_WIDTH),
4819d4fa1a1SMauro Carvalho Chehab 				crop[ATOMISP_SUBDEV_PAD_SINK]->height);
4829d4fa1a1SMauro Carvalho Chehab 		else
4839d4fa1a1SMauro Carvalho Chehab 			atomisp_css_input_set_effective_resolution(isp_sd,
4841e32f6eaSHans de Goede 				ATOMISP_INPUT_STREAM_GENERAL,
4859d4fa1a1SMauro Carvalho Chehab 				crop[ATOMISP_SUBDEV_PAD_SINK]->width,
4869d4fa1a1SMauro Carvalho Chehab 				rounddown(crop[ATOMISP_SUBDEV_PAD_SINK]->
4879d4fa1a1SMauro Carvalho Chehab 					  width * r->height / r->width,
4889d4fa1a1SMauro Carvalho Chehab 					  ATOM_ISP_STEP_WIDTH));
489ea360037SHans de Goede 	} else {
4909d4fa1a1SMauro Carvalho Chehab 		comp[pad]->width = r->width;
4919d4fa1a1SMauro Carvalho Chehab 		comp[pad]->height = r->height;
4929d4fa1a1SMauro Carvalho Chehab 	}
4939d4fa1a1SMauro Carvalho Chehab 
494ea360037SHans de Goede get_rect:
4959d4fa1a1SMauro Carvalho Chehab 	/* Set format dimensions on non-sink pads as well. */
4969d4fa1a1SMauro Carvalho Chehab 	if (pad != ATOMISP_SUBDEV_PAD_SINK) {
4979d4fa1a1SMauro Carvalho Chehab 		ffmt[pad]->width = comp[pad]->width;
4989d4fa1a1SMauro Carvalho Chehab 		ffmt[pad]->height = comp[pad]->height;
4999d4fa1a1SMauro Carvalho Chehab 	}
5009d4fa1a1SMauro Carvalho Chehab 
5010d346d2aSTomi Valkeinen 	if (!atomisp_subdev_get_rect(sd, sd_state, which, pad, target))
5029d4fa1a1SMauro Carvalho Chehab 		return -EINVAL;
5030d346d2aSTomi Valkeinen 	*r = *atomisp_subdev_get_rect(sd, sd_state, which, pad, target);
5049d4fa1a1SMauro Carvalho Chehab 
5059d4fa1a1SMauro Carvalho Chehab 	dev_dbg(isp->dev, "sel actual: l %d t %d w %d h %d\n",
5069d4fa1a1SMauro Carvalho Chehab 		r->left, r->top, r->width, r->height);
5079d4fa1a1SMauro Carvalho Chehab 
5089d4fa1a1SMauro Carvalho Chehab 	return 0;
5099d4fa1a1SMauro Carvalho Chehab }
5109d4fa1a1SMauro Carvalho Chehab 
isp_subdev_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)5119d4fa1a1SMauro Carvalho Chehab static int isp_subdev_set_selection(struct v4l2_subdev *sd,
5120d346d2aSTomi Valkeinen 				    struct v4l2_subdev_state *sd_state,
5139d4fa1a1SMauro Carvalho Chehab 				    struct v4l2_subdev_selection *sel)
5149d4fa1a1SMauro Carvalho Chehab {
5159d4fa1a1SMauro Carvalho Chehab 	int rval = isp_subdev_validate_rect(sd, sel->pad, sel->target);
5169d4fa1a1SMauro Carvalho Chehab 
5179d4fa1a1SMauro Carvalho Chehab 	if (rval)
5189d4fa1a1SMauro Carvalho Chehab 		return rval;
5199d4fa1a1SMauro Carvalho Chehab 
5200d346d2aSTomi Valkeinen 	return atomisp_subdev_set_selection(sd, sd_state, sel->which,
5210d346d2aSTomi Valkeinen 					    sel->pad,
5229d4fa1a1SMauro Carvalho Chehab 					    sel->target, sel->flags, &sel->r);
5239d4fa1a1SMauro Carvalho Chehab }
5249d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_set_ffmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,uint32_t which,u32 pad,struct v4l2_mbus_framefmt * ffmt)5259d4fa1a1SMauro Carvalho Chehab void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd,
5260d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
5270d346d2aSTomi Valkeinen 			     uint32_t which,
5289d4fa1a1SMauro Carvalho Chehab 			     u32 pad, struct v4l2_mbus_framefmt *ffmt)
5299d4fa1a1SMauro Carvalho Chehab {
5309d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
5319d4fa1a1SMauro Carvalho Chehab 	struct atomisp_device *isp = isp_sd->isp;
5329d4fa1a1SMauro Carvalho Chehab 	struct v4l2_mbus_framefmt *__ffmt =
5330d346d2aSTomi Valkeinen 	    atomisp_subdev_get_ffmt(sd, sd_state, which, pad);
5349d4fa1a1SMauro Carvalho Chehab 
5359d4fa1a1SMauro Carvalho Chehab 	dev_dbg(isp->dev, "ffmt: pad %s w %d h %d code 0x%8.8x which %s\n",
53662599316SDan Carpenter 		atomisp_pad_str(pad), ffmt->width, ffmt->height, ffmt->code,
5379d4fa1a1SMauro Carvalho Chehab 		which == V4L2_SUBDEV_FORMAT_TRY ? "V4L2_SUBDEV_FORMAT_TRY"
5389d4fa1a1SMauro Carvalho Chehab 		: "V4L2_SUBDEV_FORMAT_ACTIVE");
5399d4fa1a1SMauro Carvalho Chehab 
5409d4fa1a1SMauro Carvalho Chehab 	switch (pad) {
5419d4fa1a1SMauro Carvalho Chehab 	case ATOMISP_SUBDEV_PAD_SINK: {
5429d4fa1a1SMauro Carvalho Chehab 		const struct atomisp_in_fmt_conv *fc =
5439d4fa1a1SMauro Carvalho Chehab 		    atomisp_find_in_fmt_conv(ffmt->code);
5449d4fa1a1SMauro Carvalho Chehab 
5459d4fa1a1SMauro Carvalho Chehab 		if (!fc) {
5469d4fa1a1SMauro Carvalho Chehab 			fc = atomisp_in_fmt_conv;
5479d4fa1a1SMauro Carvalho Chehab 			ffmt->code = fc->code;
5489d4fa1a1SMauro Carvalho Chehab 			dev_dbg(isp->dev, "using 0x%8.8x instead\n",
5499d4fa1a1SMauro Carvalho Chehab 				ffmt->code);
5509d4fa1a1SMauro Carvalho Chehab 		}
5519d4fa1a1SMauro Carvalho Chehab 
5529d4fa1a1SMauro Carvalho Chehab 		*__ffmt = *ffmt;
5539d4fa1a1SMauro Carvalho Chehab 
5540d346d2aSTomi Valkeinen 		isp_subdev_propagate(sd, sd_state, which, pad,
5559d4fa1a1SMauro Carvalho Chehab 				     V4L2_SEL_TGT_CROP, 0);
5569d4fa1a1SMauro Carvalho Chehab 
5579d4fa1a1SMauro Carvalho Chehab 		if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
5589d4fa1a1SMauro Carvalho Chehab 			atomisp_css_input_set_resolution(isp_sd,
5591e32f6eaSHans de Goede 							 ATOMISP_INPUT_STREAM_GENERAL, ffmt);
5609d4fa1a1SMauro Carvalho Chehab 			atomisp_css_input_set_binning_factor(isp_sd,
5611e32f6eaSHans de Goede 							     ATOMISP_INPUT_STREAM_GENERAL,
562159a61daSHans de Goede 							     0);
5631e32f6eaSHans de Goede 			atomisp_css_input_set_bayer_order(isp_sd, ATOMISP_INPUT_STREAM_GENERAL,
5649d4fa1a1SMauro Carvalho Chehab 							  fc->bayer_order);
5651e32f6eaSHans de Goede 			atomisp_css_input_set_format(isp_sd, ATOMISP_INPUT_STREAM_GENERAL,
566fb1f6ae6SMauro Carvalho Chehab 						     fc->atomisp_in_fmt);
5671e32f6eaSHans de Goede 			atomisp_css_set_default_isys_config(isp_sd, ATOMISP_INPUT_STREAM_GENERAL,
5689d4fa1a1SMauro Carvalho Chehab 							    ffmt);
5699d4fa1a1SMauro Carvalho Chehab 		}
5709d4fa1a1SMauro Carvalho Chehab 
5719d4fa1a1SMauro Carvalho Chehab 		break;
5729d4fa1a1SMauro Carvalho Chehab 	}
573aca8a1dfSHans de Goede 	case ATOMISP_SUBDEV_PAD_SOURCE:
5749d4fa1a1SMauro Carvalho Chehab 		__ffmt->code = ffmt->code;
5759d4fa1a1SMauro Carvalho Chehab 		break;
5769d4fa1a1SMauro Carvalho Chehab 	}
5779d4fa1a1SMauro Carvalho Chehab }
5789d4fa1a1SMauro Carvalho Chehab 
5799d4fa1a1SMauro Carvalho Chehab /*
5809d4fa1a1SMauro Carvalho Chehab  * isp_subdev_get_format - Retrieve the video format on a pad
5819d4fa1a1SMauro Carvalho Chehab  * @sd : ISP V4L2 subdevice
5829d4fa1a1SMauro Carvalho Chehab  * @fh : V4L2 subdev file handle
5839d4fa1a1SMauro Carvalho Chehab  * @pad: Pad number
5849d4fa1a1SMauro Carvalho Chehab  * @fmt: Format
5859d4fa1a1SMauro Carvalho Chehab  *
5869d4fa1a1SMauro Carvalho Chehab  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
5879d4fa1a1SMauro Carvalho Chehab  * to the format type.
5889d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)5899d4fa1a1SMauro Carvalho Chehab static int isp_subdev_get_format(struct v4l2_subdev *sd,
5900d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
5919d4fa1a1SMauro Carvalho Chehab 				 struct v4l2_subdev_format *fmt)
5929d4fa1a1SMauro Carvalho Chehab {
5930d346d2aSTomi Valkeinen 	fmt->format = *atomisp_subdev_get_ffmt(sd, sd_state, fmt->which,
5940d346d2aSTomi Valkeinen 					       fmt->pad);
5959d4fa1a1SMauro Carvalho Chehab 
5969d4fa1a1SMauro Carvalho Chehab 	return 0;
5979d4fa1a1SMauro Carvalho Chehab }
5989d4fa1a1SMauro Carvalho Chehab 
5999d4fa1a1SMauro Carvalho Chehab /*
6009d4fa1a1SMauro Carvalho Chehab  * isp_subdev_set_format - Set the video format on a pad
6019d4fa1a1SMauro Carvalho Chehab  * @sd : ISP subdev V4L2 subdevice
6029d4fa1a1SMauro Carvalho Chehab  * @fh : V4L2 subdev file handle
6039d4fa1a1SMauro Carvalho Chehab  * @pad: Pad number
6049d4fa1a1SMauro Carvalho Chehab  * @fmt: Format
6059d4fa1a1SMauro Carvalho Chehab  *
6069d4fa1a1SMauro Carvalho Chehab  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
6079d4fa1a1SMauro Carvalho Chehab  * to the format type.
6089d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)6099d4fa1a1SMauro Carvalho Chehab static int isp_subdev_set_format(struct v4l2_subdev *sd,
6100d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
6119d4fa1a1SMauro Carvalho Chehab 				 struct v4l2_subdev_format *fmt)
6129d4fa1a1SMauro Carvalho Chehab {
6130d346d2aSTomi Valkeinen 	atomisp_subdev_set_ffmt(sd, sd_state, fmt->which, fmt->pad,
6140d346d2aSTomi Valkeinen 				&fmt->format);
6159d4fa1a1SMauro Carvalho Chehab 
6169d4fa1a1SMauro Carvalho Chehab 	return 0;
6179d4fa1a1SMauro Carvalho Chehab }
6189d4fa1a1SMauro Carvalho Chehab 
6199d4fa1a1SMauro Carvalho Chehab /* V4L2 subdev core operations */
6209d4fa1a1SMauro Carvalho Chehab static const struct v4l2_subdev_core_ops isp_subdev_v4l2_core_ops = {
6219d4fa1a1SMauro Carvalho Chehab 	.ioctl = isp_subdev_ioctl, .s_power = isp_subdev_set_power,
6229d4fa1a1SMauro Carvalho Chehab 	.subscribe_event = isp_subdev_subscribe_event,
6239d4fa1a1SMauro Carvalho Chehab 	.unsubscribe_event = isp_subdev_unsubscribe_event,
6249d4fa1a1SMauro Carvalho Chehab };
6259d4fa1a1SMauro Carvalho Chehab 
6269d4fa1a1SMauro Carvalho Chehab /* V4L2 subdev pad operations */
6279d4fa1a1SMauro Carvalho Chehab static const struct v4l2_subdev_pad_ops isp_subdev_v4l2_pad_ops = {
6289d4fa1a1SMauro Carvalho Chehab 	.enum_mbus_code = isp_subdev_enum_mbus_code,
6299d4fa1a1SMauro Carvalho Chehab 	.get_fmt = isp_subdev_get_format,
6309d4fa1a1SMauro Carvalho Chehab 	.set_fmt = isp_subdev_set_format,
6319d4fa1a1SMauro Carvalho Chehab 	.get_selection = isp_subdev_get_selection,
6329d4fa1a1SMauro Carvalho Chehab 	.set_selection = isp_subdev_set_selection,
6339d4fa1a1SMauro Carvalho Chehab 	.link_validate = v4l2_subdev_link_validate_default,
6349d4fa1a1SMauro Carvalho Chehab };
6359d4fa1a1SMauro Carvalho Chehab 
6369d4fa1a1SMauro Carvalho Chehab /* V4L2 subdev operations */
6379d4fa1a1SMauro Carvalho Chehab static const struct v4l2_subdev_ops isp_subdev_v4l2_ops = {
6389d4fa1a1SMauro Carvalho Chehab 	.core = &isp_subdev_v4l2_core_ops,
6399d4fa1a1SMauro Carvalho Chehab 	.pad = &isp_subdev_v4l2_pad_ops,
6409d4fa1a1SMauro Carvalho Chehab };
6419d4fa1a1SMauro Carvalho Chehab 
isp_subdev_init_params(struct atomisp_sub_device * asd)6429d4fa1a1SMauro Carvalho Chehab static void isp_subdev_init_params(struct atomisp_sub_device *asd)
6439d4fa1a1SMauro Carvalho Chehab {
6449d4fa1a1SMauro Carvalho Chehab 	unsigned int i;
6459d4fa1a1SMauro Carvalho Chehab 
6469d4fa1a1SMauro Carvalho Chehab 	/* parameters initialization */
6479d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&asd->s3a_stats);
6489d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&asd->s3a_stats_in_css);
6499d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&asd->s3a_stats_ready);
6509d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&asd->dis_stats);
6519d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&asd->dis_stats_in_css);
6529d4fa1a1SMauro Carvalho Chehab 	spin_lock_init(&asd->dis_stats_lock);
6539d4fa1a1SMauro Carvalho Chehab 	for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
6549d4fa1a1SMauro Carvalho Chehab 		INIT_LIST_HEAD(&asd->metadata[i]);
6559d4fa1a1SMauro Carvalho Chehab 		INIT_LIST_HEAD(&asd->metadata_in_css[i]);
6569d4fa1a1SMauro Carvalho Chehab 		INIT_LIST_HEAD(&asd->metadata_ready[i]);
6579d4fa1a1SMauro Carvalho Chehab 	}
6589d4fa1a1SMauro Carvalho Chehab }
6599d4fa1a1SMauro Carvalho Chehab 
6609d4fa1a1SMauro Carvalho Chehab /* media operations */
6619d4fa1a1SMauro Carvalho Chehab static const struct media_entity_operations isp_subdev_media_ops = {
6629d4fa1a1SMauro Carvalho Chehab 	.link_validate = v4l2_subdev_link_validate,
6639d4fa1a1SMauro Carvalho Chehab 	/*	 .set_power = v4l2_subdev_set_power,	*/
6649d4fa1a1SMauro Carvalho Chehab };
6659d4fa1a1SMauro Carvalho Chehab 
__atomisp_update_run_mode(struct atomisp_sub_device * asd)6669d4fa1a1SMauro Carvalho Chehab static int __atomisp_update_run_mode(struct atomisp_sub_device *asd)
6679d4fa1a1SMauro Carvalho Chehab {
6689d4fa1a1SMauro Carvalho Chehab 	struct atomisp_device *isp = asd->isp;
6699d4fa1a1SMauro Carvalho Chehab 	struct v4l2_ctrl *ctrl = asd->run_mode;
6709d4fa1a1SMauro Carvalho Chehab 	struct v4l2_ctrl *c;
6719d4fa1a1SMauro Carvalho Chehab 	s32 mode;
6729d4fa1a1SMauro Carvalho Chehab 
6739d4fa1a1SMauro Carvalho Chehab 	mode = ctrl->val;
6749d4fa1a1SMauro Carvalho Chehab 
6759d4fa1a1SMauro Carvalho Chehab 	c = v4l2_ctrl_find(
6769d4fa1a1SMauro Carvalho Chehab 		isp->inputs[asd->input_curr].camera->ctrl_handler,
6779d4fa1a1SMauro Carvalho Chehab 		V4L2_CID_RUN_MODE);
6789d4fa1a1SMauro Carvalho Chehab 
6799d4fa1a1SMauro Carvalho Chehab 	if (c)
6809d4fa1a1SMauro Carvalho Chehab 		return v4l2_ctrl_s_ctrl(c, mode);
6819d4fa1a1SMauro Carvalho Chehab 
6829d4fa1a1SMauro Carvalho Chehab 	return 0;
6839d4fa1a1SMauro Carvalho Chehab }
6849d4fa1a1SMauro Carvalho Chehab 
atomisp_update_run_mode(struct atomisp_sub_device * asd)6859d4fa1a1SMauro Carvalho Chehab int atomisp_update_run_mode(struct atomisp_sub_device *asd)
6869d4fa1a1SMauro Carvalho Chehab {
6879d4fa1a1SMauro Carvalho Chehab 	int rval;
6889d4fa1a1SMauro Carvalho Chehab 
6899d4fa1a1SMauro Carvalho Chehab 	mutex_lock(asd->ctrl_handler.lock);
6909d4fa1a1SMauro Carvalho Chehab 	rval = __atomisp_update_run_mode(asd);
6919d4fa1a1SMauro Carvalho Chehab 	mutex_unlock(asd->ctrl_handler.lock);
6929d4fa1a1SMauro Carvalho Chehab 
6939d4fa1a1SMauro Carvalho Chehab 	return rval;
6949d4fa1a1SMauro Carvalho Chehab }
6959d4fa1a1SMauro Carvalho Chehab 
s_ctrl(struct v4l2_ctrl * ctrl)6969d4fa1a1SMauro Carvalho Chehab static int s_ctrl(struct v4l2_ctrl *ctrl)
6979d4fa1a1SMauro Carvalho Chehab {
6989d4fa1a1SMauro Carvalho Chehab 	struct atomisp_sub_device *asd = container_of(
6999d4fa1a1SMauro Carvalho Chehab 					     ctrl->handler, struct atomisp_sub_device, ctrl_handler);
7009d4fa1a1SMauro Carvalho Chehab 	switch (ctrl->id) {
7019d4fa1a1SMauro Carvalho Chehab 	case V4L2_CID_RUN_MODE:
7029d4fa1a1SMauro Carvalho Chehab 		return __atomisp_update_run_mode(asd);
7039d4fa1a1SMauro Carvalho Chehab 	}
7049d4fa1a1SMauro Carvalho Chehab 
7059d4fa1a1SMauro Carvalho Chehab 	return 0;
7069d4fa1a1SMauro Carvalho Chehab }
7079d4fa1a1SMauro Carvalho Chehab 
7089d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_ops ctrl_ops = {
7099d4fa1a1SMauro Carvalho Chehab 	.s_ctrl = &s_ctrl,
7109d4fa1a1SMauro Carvalho Chehab };
7119d4fa1a1SMauro Carvalho Chehab 
7129d4fa1a1SMauro Carvalho Chehab static const char *const ctrl_run_mode_menu[] = {
7137943916bSHans de Goede 	[ATOMISP_RUN_MODE_VIDEO]		= "Video",
7147943916bSHans de Goede 	[ATOMISP_RUN_MODE_STILL_CAPTURE]	= "Still capture",
7157943916bSHans de Goede 	[ATOMISP_RUN_MODE_PREVIEW]		= "Preview",
7169d4fa1a1SMauro Carvalho Chehab };
7179d4fa1a1SMauro Carvalho Chehab 
7189d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_run_mode = {
7199d4fa1a1SMauro Carvalho Chehab 	.ops = &ctrl_ops,
7209d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_RUN_MODE,
7219d4fa1a1SMauro Carvalho Chehab 	.name = "Atomisp run mode",
7229d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_MENU,
7237943916bSHans de Goede 	.min = ATOMISP_RUN_MODE_MIN,
7247943916bSHans de Goede 	.def = ATOMISP_RUN_MODE_PREVIEW,
7257943916bSHans de Goede 	.max = ATOMISP_RUN_MODE_MAX,
7269d4fa1a1SMauro Carvalho Chehab 	.qmenu = ctrl_run_mode_menu,
7279d4fa1a1SMauro Carvalho Chehab };
7289d4fa1a1SMauro Carvalho Chehab 
7299d4fa1a1SMauro Carvalho Chehab static const char *const ctrl_vfpp_mode_menu[] = {
7309d4fa1a1SMauro Carvalho Chehab 	"Enable",			/* vfpp always enabled */
7319d4fa1a1SMauro Carvalho Chehab 	"Disable to scaler mode",	/* CSS into video mode and disable */
7329d4fa1a1SMauro Carvalho Chehab 	"Disable to low latency mode",	/* CSS into still mode and disable */
7339d4fa1a1SMauro Carvalho Chehab };
7349d4fa1a1SMauro Carvalho Chehab 
7359d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_vfpp = {
7369d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_VFPP,
7379d4fa1a1SMauro Carvalho Chehab 	.name = "Atomisp vf postprocess",
7389d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_MENU,
7399d4fa1a1SMauro Carvalho Chehab 	.min = 0,
7409d4fa1a1SMauro Carvalho Chehab 	.def = 0,
7419d4fa1a1SMauro Carvalho Chehab 	.max = 2,
7429d4fa1a1SMauro Carvalho Chehab 	.qmenu = ctrl_vfpp_mode_menu,
7439d4fa1a1SMauro Carvalho Chehab };
7449d4fa1a1SMauro Carvalho Chehab 
7459d4fa1a1SMauro Carvalho Chehab /*
7469d4fa1a1SMauro Carvalho Chehab  * Control for continuous mode raw buffer size
7479d4fa1a1SMauro Carvalho Chehab  *
7489d4fa1a1SMauro Carvalho Chehab  * The size of the RAW ringbuffer sets limit on how much
7499d4fa1a1SMauro Carvalho Chehab  * back in time application can go when requesting capture
7509d4fa1a1SMauro Carvalho Chehab  * frames to be rendered, and how many frames can be rendered
7519d4fa1a1SMauro Carvalho Chehab  * in a burst at full sensor rate.
7529d4fa1a1SMauro Carvalho Chehab  *
7539d4fa1a1SMauro Carvalho Chehab  * Note: this setting has a big impact on memory consumption of
7549d4fa1a1SMauro Carvalho Chehab  * the CSS subsystem.
7559d4fa1a1SMauro Carvalho Chehab  */
7569d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_continuous_raw_buffer_size = {
7579d4fa1a1SMauro Carvalho Chehab 	.ops = &ctrl_ops,
7589d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_ATOMISP_CONTINUOUS_RAW_BUFFER_SIZE,
7599d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_INTEGER,
7609d4fa1a1SMauro Carvalho Chehab 	.name = "Continuous raw ringbuffer size",
7619d4fa1a1SMauro Carvalho Chehab 	.min = 1,
7629d4fa1a1SMauro Carvalho Chehab 	.max = 100, /* depends on CSS version, runtime checked */
7639d4fa1a1SMauro Carvalho Chehab 	.step = 1,
7649d4fa1a1SMauro Carvalho Chehab 	.def = 3,
7659d4fa1a1SMauro Carvalho Chehab };
7669d4fa1a1SMauro Carvalho Chehab 
7679d4fa1a1SMauro Carvalho Chehab /*
7689d4fa1a1SMauro Carvalho Chehab  * Control for enabling continuous viewfinder
7699d4fa1a1SMauro Carvalho Chehab  *
7709d4fa1a1SMauro Carvalho Chehab  * When enabled, and ISP is in continuous mode (see ctrl_continuous_mode ),
7719d4fa1a1SMauro Carvalho Chehab  * preview pipeline continues concurrently with capture
7729d4fa1a1SMauro Carvalho Chehab  * processing. When disabled, and continuous mode is used,
7739d4fa1a1SMauro Carvalho Chehab  * preview is paused while captures are processed, but
7749d4fa1a1SMauro Carvalho Chehab  * full pipeline restart is not needed.
7759d4fa1a1SMauro Carvalho Chehab  *
7769d4fa1a1SMauro Carvalho Chehab  * By setting this to disabled, capture processing is
7779d4fa1a1SMauro Carvalho Chehab  * essentially given priority over preview, and the effective
7789d4fa1a1SMauro Carvalho Chehab  * capture output rate may be higher than with continuous
7799d4fa1a1SMauro Carvalho Chehab  * viewfinder enabled.
7809d4fa1a1SMauro Carvalho Chehab  */
7819d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_continuous_viewfinder = {
7829d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_ATOMISP_CONTINUOUS_VIEWFINDER,
7839d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_BOOLEAN,
7849d4fa1a1SMauro Carvalho Chehab 	.name = "Continuous viewfinder",
7859d4fa1a1SMauro Carvalho Chehab 	.min = 0,
7869d4fa1a1SMauro Carvalho Chehab 	.max = 1,
7879d4fa1a1SMauro Carvalho Chehab 	.step = 1,
7889d4fa1a1SMauro Carvalho Chehab 	.def = 0,
7899d4fa1a1SMauro Carvalho Chehab };
7909d4fa1a1SMauro Carvalho Chehab 
7919d4fa1a1SMauro Carvalho Chehab /*
7929d4fa1a1SMauro Carvalho Chehab  * Control for enabling Lock&Unlock Raw Buffer mechanism
7939d4fa1a1SMauro Carvalho Chehab  *
7949d4fa1a1SMauro Carvalho Chehab  * When enabled, Raw Buffer can be locked and unlocked.
7959d4fa1a1SMauro Carvalho Chehab  * Application can hold the exp_id of Raw Buffer
7969d4fa1a1SMauro Carvalho Chehab  * and unlock it when no longer needed.
7979d4fa1a1SMauro Carvalho Chehab  * Note: Make sure set this configuration before creating stream.
7989d4fa1a1SMauro Carvalho Chehab  */
7999d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_enable_raw_buffer_lock = {
8009d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_ENABLE_RAW_BUFFER_LOCK,
8019d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_BOOLEAN,
8029d4fa1a1SMauro Carvalho Chehab 	.name = "Lock Unlock Raw Buffer",
8039d4fa1a1SMauro Carvalho Chehab 	.min = 0,
8049d4fa1a1SMauro Carvalho Chehab 	.max = 1,
8059d4fa1a1SMauro Carvalho Chehab 	.step = 1,
8069d4fa1a1SMauro Carvalho Chehab 	.def = 0,
8079d4fa1a1SMauro Carvalho Chehab };
8089d4fa1a1SMauro Carvalho Chehab 
8099d4fa1a1SMauro Carvalho Chehab /*
8109d4fa1a1SMauro Carvalho Chehab  * Control to disable digital zoom of the whole stream
8119d4fa1a1SMauro Carvalho Chehab  *
8129d4fa1a1SMauro Carvalho Chehab  * When it is true, pipe configuration enable_dz will be set to false.
8139d4fa1a1SMauro Carvalho Chehab  * This can help get a better performance by disabling pp binary.
8149d4fa1a1SMauro Carvalho Chehab  *
8159d4fa1a1SMauro Carvalho Chehab  * Note: Make sure set this configuration before creating stream.
8169d4fa1a1SMauro Carvalho Chehab  */
8179d4fa1a1SMauro Carvalho Chehab static const struct v4l2_ctrl_config ctrl_disable_dz = {
8189d4fa1a1SMauro Carvalho Chehab 	.id = V4L2_CID_DISABLE_DZ,
8199d4fa1a1SMauro Carvalho Chehab 	.type = V4L2_CTRL_TYPE_BOOLEAN,
8209d4fa1a1SMauro Carvalho Chehab 	.name = "Disable digital zoom",
8219d4fa1a1SMauro Carvalho Chehab 	.min = 0,
8229d4fa1a1SMauro Carvalho Chehab 	.max = 1,
8239d4fa1a1SMauro Carvalho Chehab 	.step = 1,
8249d4fa1a1SMauro Carvalho Chehab 	.def = 0,
8259d4fa1a1SMauro Carvalho Chehab };
8269d4fa1a1SMauro Carvalho Chehab 
atomisp_init_subdev_pipe(struct atomisp_sub_device * asd,struct atomisp_video_pipe * pipe,enum v4l2_buf_type buf_type)82720734fcaSHans de Goede static int atomisp_init_subdev_pipe(struct atomisp_sub_device *asd,
8289d4fa1a1SMauro Carvalho Chehab 				    struct atomisp_video_pipe *pipe, enum v4l2_buf_type buf_type)
8299d4fa1a1SMauro Carvalho Chehab {
83020734fcaSHans de Goede 	int ret;
83120734fcaSHans de Goede 
8329d4fa1a1SMauro Carvalho Chehab 	pipe->type = buf_type;
8339d4fa1a1SMauro Carvalho Chehab 	pipe->asd = asd;
8349d4fa1a1SMauro Carvalho Chehab 	pipe->isp = asd->isp;
8359d4fa1a1SMauro Carvalho Chehab 	spin_lock_init(&pipe->irq_lock);
836cb48ae89SHans de Goede 	mutex_init(&pipe->vb_queue_mutex);
83720734fcaSHans de Goede 
83820734fcaSHans de Goede 	/* Init videobuf2 queue structure */
83920734fcaSHans de Goede 	pipe->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
84020734fcaSHans de Goede 	pipe->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR;
84120734fcaSHans de Goede 	pipe->vb_queue.buf_struct_size = sizeof(struct ia_css_frame);
84220734fcaSHans de Goede 	pipe->vb_queue.ops = &atomisp_vb2_ops;
84320734fcaSHans de Goede 	pipe->vb_queue.mem_ops = &vb2_vmalloc_memops;
84420734fcaSHans de Goede 	pipe->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
84520734fcaSHans de Goede 	ret = vb2_queue_init(&pipe->vb_queue);
84620734fcaSHans de Goede 	if (ret)
84720734fcaSHans de Goede 		return ret;
84820734fcaSHans de Goede 
84920734fcaSHans de Goede 	pipe->vdev.queue = &pipe->vb_queue;
85020734fcaSHans de Goede 	pipe->vdev.queue->lock = &pipe->vb_queue_mutex;
85120734fcaSHans de Goede 
85289f9829cSHans de Goede 	INIT_LIST_HEAD(&pipe->buffers_in_css);
8539d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&pipe->activeq);
8549d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&pipe->buffers_waiting_for_param);
8559d4fa1a1SMauro Carvalho Chehab 	INIT_LIST_HEAD(&pipe->per_frame_params);
85620734fcaSHans de Goede 
85720734fcaSHans de Goede 	return 0;
8589d4fa1a1SMauro Carvalho Chehab }
8599d4fa1a1SMauro Carvalho Chehab 
8609d4fa1a1SMauro Carvalho Chehab /*
8619d4fa1a1SMauro Carvalho Chehab  * isp_subdev_init_entities - Initialize V4L2 subdev and media entity
8629d4fa1a1SMauro Carvalho Chehab  * @asd: ISP CCDC module
8639d4fa1a1SMauro Carvalho Chehab  *
8649d4fa1a1SMauro Carvalho Chehab  * Return 0 on success and a negative error code on failure.
8659d4fa1a1SMauro Carvalho Chehab  */
isp_subdev_init_entities(struct atomisp_sub_device * asd)8669d4fa1a1SMauro Carvalho Chehab static int isp_subdev_init_entities(struct atomisp_sub_device *asd)
8679d4fa1a1SMauro Carvalho Chehab {
8689d4fa1a1SMauro Carvalho Chehab 	struct v4l2_subdev *sd = &asd->subdev;
8699d4fa1a1SMauro Carvalho Chehab 	struct media_pad *pads = asd->pads;
8709d4fa1a1SMauro Carvalho Chehab 	struct media_entity *me = &sd->entity;
8719d4fa1a1SMauro Carvalho Chehab 	int ret;
8729d4fa1a1SMauro Carvalho Chehab 
8739d4fa1a1SMauro Carvalho Chehab 	v4l2_subdev_init(sd, &isp_subdev_v4l2_ops);
8742f2fce05SHans de Goede 	sprintf(sd->name, "ATOMISP_SUBDEV");
8759d4fa1a1SMauro Carvalho Chehab 	v4l2_set_subdevdata(sd, asd);
8769d4fa1a1SMauro Carvalho Chehab 	sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
877250781c5SHans de Goede 	sd->devnode = &asd->video_out.vdev;
8789d4fa1a1SMauro Carvalho Chehab 
8799d4fa1a1SMauro Carvalho Chehab 	pads[ATOMISP_SUBDEV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
880aca8a1dfSHans de Goede 	pads[ATOMISP_SUBDEV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
8819d4fa1a1SMauro Carvalho Chehab 
882aca8a1dfSHans de Goede 	asd->fmt[ATOMISP_SUBDEV_PAD_SINK].fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10;
883aca8a1dfSHans de Goede 	asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE].fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10;
8849d4fa1a1SMauro Carvalho Chehab 
8859d4fa1a1SMauro Carvalho Chehab 	me->ops = &isp_subdev_media_ops;
886c4706036SHans de Goede 	me->function = MEDIA_ENT_F_PROC_VIDEO_ISP;
8879d4fa1a1SMauro Carvalho Chehab 	ret = media_entity_pads_init(me, ATOMISP_SUBDEV_PADS_NUM, pads);
8889d4fa1a1SMauro Carvalho Chehab 	if (ret < 0)
8899d4fa1a1SMauro Carvalho Chehab 		return ret;
8909d4fa1a1SMauro Carvalho Chehab 
891aca8a1dfSHans de Goede 	ret = atomisp_init_subdev_pipe(asd, &asd->video_out, V4L2_BUF_TYPE_VIDEO_CAPTURE);
89220734fcaSHans de Goede 	if (ret)
89320734fcaSHans de Goede 		return ret;
8949d4fa1a1SMauro Carvalho Chehab 
89545b4ab38SHans de Goede 	ret = atomisp_video_init(&asd->video_out);
8969d4fa1a1SMauro Carvalho Chehab 	if (ret < 0)
8979d4fa1a1SMauro Carvalho Chehab 		return ret;
8989d4fa1a1SMauro Carvalho Chehab 
8999d4fa1a1SMauro Carvalho Chehab 	ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, 1);
9009d4fa1a1SMauro Carvalho Chehab 	if (ret)
9019d4fa1a1SMauro Carvalho Chehab 		return ret;
9029d4fa1a1SMauro Carvalho Chehab 
9039d4fa1a1SMauro Carvalho Chehab 	asd->run_mode = v4l2_ctrl_new_custom(&asd->ctrl_handler,
9049d4fa1a1SMauro Carvalho Chehab 					     &ctrl_run_mode, NULL);
9059d4fa1a1SMauro Carvalho Chehab 	asd->vfpp = v4l2_ctrl_new_custom(&asd->ctrl_handler,
9069d4fa1a1SMauro Carvalho Chehab 					 &ctrl_vfpp, NULL);
9079d4fa1a1SMauro Carvalho Chehab 	asd->continuous_viewfinder = v4l2_ctrl_new_custom(&asd->ctrl_handler,
9089d4fa1a1SMauro Carvalho Chehab 				     &ctrl_continuous_viewfinder,
9099d4fa1a1SMauro Carvalho Chehab 				     NULL);
9109d4fa1a1SMauro Carvalho Chehab 	asd->continuous_raw_buffer_size =
9119d4fa1a1SMauro Carvalho Chehab 	    v4l2_ctrl_new_custom(&asd->ctrl_handler,
9129d4fa1a1SMauro Carvalho Chehab 				 &ctrl_continuous_raw_buffer_size,
9139d4fa1a1SMauro Carvalho Chehab 				 NULL);
9149d4fa1a1SMauro Carvalho Chehab 
9159d4fa1a1SMauro Carvalho Chehab 	asd->enable_raw_buffer_lock =
9169d4fa1a1SMauro Carvalho Chehab 	    v4l2_ctrl_new_custom(&asd->ctrl_handler,
9179d4fa1a1SMauro Carvalho Chehab 				 &ctrl_enable_raw_buffer_lock,
9189d4fa1a1SMauro Carvalho Chehab 				 NULL);
9199d4fa1a1SMauro Carvalho Chehab 	asd->disable_dz =
9209d4fa1a1SMauro Carvalho Chehab 	    v4l2_ctrl_new_custom(&asd->ctrl_handler,
9219d4fa1a1SMauro Carvalho Chehab 				 &ctrl_disable_dz,
9229d4fa1a1SMauro Carvalho Chehab 				 NULL);
9239d4fa1a1SMauro Carvalho Chehab 
9249d4fa1a1SMauro Carvalho Chehab 	/* Make controls visible on subdev as well. */
9259d4fa1a1SMauro Carvalho Chehab 	asd->subdev.ctrl_handler = &asd->ctrl_handler;
9269d4fa1a1SMauro Carvalho Chehab 	spin_lock_init(&asd->raw_buffer_bitmap_lock);
9279d4fa1a1SMauro Carvalho Chehab 	return asd->ctrl_handler.error;
9289d4fa1a1SMauro Carvalho Chehab }
9299d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_cleanup_entities(struct atomisp_sub_device * asd)9309d4fa1a1SMauro Carvalho Chehab static void atomisp_subdev_cleanup_entities(struct atomisp_sub_device *asd)
9319d4fa1a1SMauro Carvalho Chehab {
9329d4fa1a1SMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&asd->ctrl_handler);
9339d4fa1a1SMauro Carvalho Chehab 
9349d4fa1a1SMauro Carvalho Chehab 	media_entity_cleanup(&asd->subdev.entity);
9359d4fa1a1SMauro Carvalho Chehab }
9369d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device * asd)9379d4fa1a1SMauro Carvalho Chehab void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd)
9389d4fa1a1SMauro Carvalho Chehab {
9399d4fa1a1SMauro Carvalho Chehab 	struct v4l2_fh *fh, *fh_tmp;
9409d4fa1a1SMauro Carvalho Chehab 	struct v4l2_event event;
9419d4fa1a1SMauro Carvalho Chehab 	unsigned int i, pending_event;
9429d4fa1a1SMauro Carvalho Chehab 
9439d4fa1a1SMauro Carvalho Chehab 	list_for_each_entry_safe(fh, fh_tmp,
9449d4fa1a1SMauro Carvalho Chehab 				 &asd->subdev.devnode->fh_list, list) {
9459d4fa1a1SMauro Carvalho Chehab 		pending_event = v4l2_event_pending(fh);
9469d4fa1a1SMauro Carvalho Chehab 		for (i = 0; i < pending_event; i++)
9479d4fa1a1SMauro Carvalho Chehab 			v4l2_event_dequeue(fh, &event, 1);
9489d4fa1a1SMauro Carvalho Chehab 	}
9499d4fa1a1SMauro Carvalho Chehab }
9509d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_unregister_entities(struct atomisp_sub_device * asd)9519d4fa1a1SMauro Carvalho Chehab void atomisp_subdev_unregister_entities(struct atomisp_sub_device *asd)
9529d4fa1a1SMauro Carvalho Chehab {
9539d4fa1a1SMauro Carvalho Chehab 	atomisp_subdev_cleanup_entities(asd);
9549d4fa1a1SMauro Carvalho Chehab 	v4l2_device_unregister_subdev(&asd->subdev);
955aca8a1dfSHans de Goede 	atomisp_video_unregister(&asd->video_out);
9569d4fa1a1SMauro Carvalho Chehab }
9579d4fa1a1SMauro Carvalho Chehab 
atomisp_subdev_register_subdev(struct atomisp_sub_device * asd,struct v4l2_device * vdev)9583b423e1bSHans de Goede int atomisp_subdev_register_subdev(struct atomisp_sub_device *asd,
9593b423e1bSHans de Goede 				   struct v4l2_device *vdev)
9603b423e1bSHans de Goede {
9613b423e1bSHans de Goede 	return v4l2_device_register_subdev(vdev, &asd->subdev);
9623b423e1bSHans de Goede }
9633b423e1bSHans de Goede 
9649d4fa1a1SMauro Carvalho Chehab /*
9659d4fa1a1SMauro Carvalho Chehab  * atomisp_subdev_init - ISP Subdevice  initialization.
9669d4fa1a1SMauro Carvalho Chehab  * @dev: Device pointer specific to the ATOM ISP.
9679d4fa1a1SMauro Carvalho Chehab  *
9689d4fa1a1SMauro Carvalho Chehab  * TODO: Get the initialisation values from platform data.
9699d4fa1a1SMauro Carvalho Chehab  *
9709d4fa1a1SMauro Carvalho Chehab  * Return 0 on success or a negative error code otherwise.
9719d4fa1a1SMauro Carvalho Chehab  */
atomisp_subdev_init(struct atomisp_device * isp)9729d4fa1a1SMauro Carvalho Chehab int atomisp_subdev_init(struct atomisp_device *isp)
9739d4fa1a1SMauro Carvalho Chehab {
974401e0e41SHans de Goede 	int ret;
9759d4fa1a1SMauro Carvalho Chehab 
976401e0e41SHans de Goede 	isp->asd.isp = isp;
977401e0e41SHans de Goede 	isp_subdev_init_params(&isp->asd);
978401e0e41SHans de Goede 	ret = isp_subdev_init_entities(&isp->asd);
979401e0e41SHans de Goede 	if (ret < 0)
980401e0e41SHans de Goede 		atomisp_subdev_cleanup_entities(&isp->asd);
9819d4fa1a1SMauro Carvalho Chehab 
9829d4fa1a1SMauro Carvalho Chehab 	return ret;
9839d4fa1a1SMauro Carvalho Chehab }
984