xref: /linux/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
107fc05bdSLad Prabhakar // SPDX-License-Identifier: GPL-2.0
207fc05bdSLad Prabhakar /*
307fc05bdSLad Prabhakar  * Driver for Renesas RZ/G2L CRU
407fc05bdSLad Prabhakar  *
507fc05bdSLad Prabhakar  * Copyright (C) 2022 Renesas Electronics Corp.
607fc05bdSLad Prabhakar  */
707fc05bdSLad Prabhakar 
89c7fa014SBiju Das #include <linux/delay.h>
9b56dccafSLad Prabhakar #include <media/mipi-csi2.h>
10b56dccafSLad Prabhakar 
1107fc05bdSLad Prabhakar #include "rzg2l-cru.h"
12c0fc8dd0SLad Prabhakar #include "rzg2l-cru-regs.h"
1307fc05bdSLad Prabhakar 
1407fc05bdSLad Prabhakar static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
15b56dccafSLad Prabhakar 	{
16b56dccafSLad Prabhakar 		.codes = {
17b56dccafSLad Prabhakar 			MEDIA_BUS_FMT_UYVY8_1X16,
188853467cSLad Prabhakar 		},
198853467cSLad Prabhakar 		.datatype = MIPI_CSI2_DT_YUV422_8B,
20c6ed80fdSLad Prabhakar 		.format = V4L2_PIX_FMT_UYVY,
212269e399SLad Prabhakar 		.icndmr = ICnDMR_YCMODE_UYVY,
22b56dccafSLad Prabhakar 		.yuv = true,
230477b086SLad Prabhakar 	},
240477b086SLad Prabhakar 	{
250477b086SLad Prabhakar 		.codes = {
260477b086SLad Prabhakar 			MEDIA_BUS_FMT_SBGGR8_1X8,
270477b086SLad Prabhakar 		},
280477b086SLad Prabhakar 		.format = V4L2_PIX_FMT_SBGGR8,
292269e399SLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW8,
300477b086SLad Prabhakar 		.icndmr = 0,
310477b086SLad Prabhakar 		.yuv = false,
320477b086SLad Prabhakar 	},
330477b086SLad Prabhakar 	{
340477b086SLad Prabhakar 		.codes = {
350477b086SLad Prabhakar 			MEDIA_BUS_FMT_SGBRG8_1X8,
360477b086SLad Prabhakar 		},
372269e399SLad Prabhakar 		.format = V4L2_PIX_FMT_SGBRG8,
380477b086SLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW8,
390477b086SLad Prabhakar 		.icndmr = 0,
400477b086SLad Prabhakar 		.yuv = false,
410477b086SLad Prabhakar 	},
420477b086SLad Prabhakar 	{
430477b086SLad Prabhakar 		.codes = {
440477b086SLad Prabhakar 			MEDIA_BUS_FMT_SGRBG8_1X8,
452269e399SLad Prabhakar 		},
460477b086SLad Prabhakar 		.format = V4L2_PIX_FMT_SGRBG8,
470477b086SLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW8,
480477b086SLad Prabhakar 		.icndmr = 0,
490477b086SLad Prabhakar 		.yuv = false,
500477b086SLad Prabhakar 	},
510477b086SLad Prabhakar 	{
520477b086SLad Prabhakar 		.codes = {
532269e399SLad Prabhakar 			MEDIA_BUS_FMT_SRGGB8_1X8,
540477b086SLad Prabhakar 		},
5507fc05bdSLad Prabhakar 		.format = V4L2_PIX_FMT_SRGGB8,
5607fc05bdSLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW8,
57b56dccafSLad Prabhakar 		.icndmr = 0,
5807fc05bdSLad Prabhakar 		.yuv = false,
5907fc05bdSLad Prabhakar 	},
6007fc05bdSLad Prabhakar 	{
6107fc05bdSLad Prabhakar 		.codes = {
6207fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SBGGR10_1X10,
6307fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SGBRG10_1X10,
6407fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SGRBG10_1X10,
6507fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SRGGB10_1X10
6607fc05bdSLad Prabhakar 		},
6707fc05bdSLad Prabhakar 		.format = V4L2_PIX_FMT_RAW_CRU10,
688853467cSLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW10,
698853467cSLad Prabhakar 		.icndmr = 0,
708853467cSLad Prabhakar 		.yuv = false,
718853467cSLad Prabhakar 	},
728853467cSLad Prabhakar 	{
738853467cSLad Prabhakar 		.codes = {
748853467cSLad Prabhakar 			MEDIA_BUS_FMT_SBGGR12_1X12,
758853467cSLad Prabhakar 			MEDIA_BUS_FMT_SGBRG12_1X12,
768853467cSLad Prabhakar 			MEDIA_BUS_FMT_SGRBG12_1X12,
778853467cSLad Prabhakar 			MEDIA_BUS_FMT_SRGGB12_1X12
788853467cSLad Prabhakar 		},
798853467cSLad Prabhakar 		.format = V4L2_PIX_FMT_RAW_CRU12,
808853467cSLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW12,
818853467cSLad Prabhakar 		.icndmr = 0,
828853467cSLad Prabhakar 		.yuv = false,
838853467cSLad Prabhakar 	},
848853467cSLad Prabhakar 	{
858853467cSLad Prabhakar 		.codes = {
868853467cSLad Prabhakar 			MEDIA_BUS_FMT_SBGGR14_1X14,
878853467cSLad Prabhakar 			MEDIA_BUS_FMT_SGBRG14_1X14,
8807fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SGRBG14_1X14,
8907fc05bdSLad Prabhakar 			MEDIA_BUS_FMT_SRGGB14_1X14
9007fc05bdSLad Prabhakar 		},
9107fc05bdSLad Prabhakar 		.format = V4L2_PIX_FMT_RAW_CRU14,
9207fc05bdSLad Prabhakar 		.datatype = MIPI_CSI2_DT_RAW14,
9307fc05bdSLad Prabhakar 		.icndmr = 0,
94bc0e8d91SSakari Ailus 		.yuv = false,
9507fc05bdSLad Prabhakar 	},
9607fc05bdSLad Prabhakar };
9707fc05bdSLad Prabhakar 
rzg2l_cru_ip_code_to_fmt(unsigned int code)9807fc05bdSLad Prabhakar const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code)
9907fc05bdSLad Prabhakar {
10007fc05bdSLad Prabhakar 	unsigned int i, j;
10107fc05bdSLad Prabhakar 
10207fc05bdSLad Prabhakar 	for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
10307fc05bdSLad Prabhakar 		for (j = 0; j < ARRAY_SIZE(rzg2l_cru_ip_formats[i].codes); j++) {
10407fc05bdSLad Prabhakar 			if (rzg2l_cru_ip_formats[i].codes[j] == code)
10507fc05bdSLad Prabhakar 				return &rzg2l_cru_ip_formats[i];
10607fc05bdSLad Prabhakar 		}
10707fc05bdSLad Prabhakar 	}
10807fc05bdSLad Prabhakar 
10907fc05bdSLad Prabhakar 	return NULL;
11007fc05bdSLad Prabhakar }
11107fc05bdSLad Prabhakar 
rzg2l_cru_ip_format_to_fmt(u32 format)11207fc05bdSLad Prabhakar const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format)
11307fc05bdSLad Prabhakar {
11407fc05bdSLad Prabhakar 	unsigned int i;
11507fc05bdSLad Prabhakar 
11607fc05bdSLad Prabhakar 	for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
11707fc05bdSLad Prabhakar 		if (rzg2l_cru_ip_formats[i].format == format)
11807fc05bdSLad Prabhakar 			return &rzg2l_cru_ip_formats[i];
11907fc05bdSLad Prabhakar 	}
12007fc05bdSLad Prabhakar 
12107fc05bdSLad Prabhakar 	return NULL;
12207fc05bdSLad Prabhakar }
12307fc05bdSLad Prabhakar 
rzg2l_cru_ip_index_to_fmt(u32 index)12407fc05bdSLad Prabhakar const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index)
12507fc05bdSLad Prabhakar {
1269c7fa014SBiju Das 	if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats))
1279c7fa014SBiju Das 		return NULL;
12807fc05bdSLad Prabhakar 
12907fc05bdSLad Prabhakar 	return &rzg2l_cru_ip_formats[index];
13007fc05bdSLad Prabhakar }
13107fc05bdSLad Prabhakar 
rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format * fmt,unsigned int code)13207fc05bdSLad Prabhakar bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt,
13307fc05bdSLad Prabhakar 					 unsigned int code)
13407fc05bdSLad Prabhakar {
135fdc7bd5bSBiju Das 	unsigned int i;
13607fc05bdSLad Prabhakar 
13707fc05bdSLad Prabhakar 	for (i = 0; i < ARRAY_SIZE(fmt->codes); i++)
13807fc05bdSLad Prabhakar 		if (fmt->codes[i] == code)
13907fc05bdSLad Prabhakar 			return true;
14007fc05bdSLad Prabhakar 
14107fc05bdSLad Prabhakar 	return false;
14207fc05bdSLad Prabhakar }
rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev * cru)14307fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru)
14407fc05bdSLad Prabhakar {
14507fc05bdSLad Prabhakar 	struct v4l2_subdev_state *state;
14607fc05bdSLad Prabhakar 	struct v4l2_mbus_framefmt *fmt;
14707fc05bdSLad Prabhakar 
14807fc05bdSLad Prabhakar 	state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev);
14907fc05bdSLad Prabhakar 	fmt = v4l2_subdev_state_get_format(state, 1);
15007fc05bdSLad Prabhakar 	v4l2_subdev_unlock_state(state);
151*5f5ed645SLad Prabhakar 
152*5f5ed645SLad Prabhakar 	return fmt;
15307fc05bdSLad Prabhakar }
15407fc05bdSLad Prabhakar 
rzg2l_cru_ip_s_stream(struct v4l2_subdev * sd,int enable)15507fc05bdSLad Prabhakar static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable)
156bc0e8d91SSakari Ailus {
15707fc05bdSLad Prabhakar 	struct rzg2l_cru_dev *cru;
15807fc05bdSLad Prabhakar 	int s_stream_ret = 0;
15907fc05bdSLad Prabhakar 	int ret;
16007fc05bdSLad Prabhakar 
16107fc05bdSLad Prabhakar 	cru = v4l2_get_subdevdata(sd);
162bc0e8d91SSakari Ailus 
16307fc05bdSLad Prabhakar 	if (!enable) {
16407fc05bdSLad Prabhakar 		ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable);
16507fc05bdSLad Prabhakar 		if (ret)
16607fc05bdSLad Prabhakar 			s_stream_ret = ret;
16707fc05bdSLad Prabhakar 
16807fc05bdSLad Prabhakar 		ret = v4l2_subdev_call(cru->ip.remote, video, post_streamoff);
16907fc05bdSLad Prabhakar 		if (ret == -ENOIOCTLCMD)
17007fc05bdSLad Prabhakar 			ret = 0;
17107fc05bdSLad Prabhakar 		if (ret && !s_stream_ret)
17207fc05bdSLad Prabhakar 			s_stream_ret = ret;
17307fc05bdSLad Prabhakar 		rzg2l_cru_stop_image_processing(cru);
17407fc05bdSLad Prabhakar 	} else {
175*5f5ed645SLad Prabhakar 		ret = v4l2_subdev_call(cru->ip.remote, video, pre_streamon, 0);
17607fc05bdSLad Prabhakar 		if (ret == -ENOIOCTLCMD)
177*5f5ed645SLad Prabhakar 			ret = 0;
17807fc05bdSLad Prabhakar 		if (ret)
17907fc05bdSLad Prabhakar 			return ret;
18007fc05bdSLad Prabhakar 
18107fc05bdSLad Prabhakar 		fsleep(1000);
18207fc05bdSLad Prabhakar 
18307fc05bdSLad Prabhakar 		ret = rzg2l_cru_start_image_processing(cru);
18407fc05bdSLad Prabhakar 		if (ret) {
18507fc05bdSLad Prabhakar 			v4l2_subdev_call(cru->ip.remote, video, post_streamoff);
18607fc05bdSLad Prabhakar 			return ret;
18707fc05bdSLad Prabhakar 		}
18807fc05bdSLad Prabhakar 
18907fc05bdSLad Prabhakar 		ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable);
19007fc05bdSLad Prabhakar 		if (!ret || ret == -ENOIOCTLCMD)
19107fc05bdSLad Prabhakar 			return 0;
19207fc05bdSLad Prabhakar 
19307fc05bdSLad Prabhakar 		s_stream_ret = ret;
19407fc05bdSLad Prabhakar 
19507fc05bdSLad Prabhakar 		v4l2_subdev_call(cru->ip.remote, video, post_streamoff);
19607fc05bdSLad Prabhakar 		rzg2l_cru_stop_image_processing(cru);
19707fc05bdSLad Prabhakar 	}
19807fc05bdSLad Prabhakar 
19907fc05bdSLad Prabhakar 	return s_stream_ret;
20007fc05bdSLad Prabhakar }
20107fc05bdSLad Prabhakar 
rzg2l_cru_ip_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_format * fmt)202*5f5ed645SLad Prabhakar static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
203*5f5ed645SLad Prabhakar 				   struct v4l2_subdev_state *state,
204*5f5ed645SLad Prabhakar 				   struct v4l2_subdev_format *fmt)
20507fc05bdSLad Prabhakar {
20607fc05bdSLad Prabhakar 	struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
20707fc05bdSLad Prabhakar 	const struct rzg2l_cru_info *info = cru->info;
208ec37ac1aSLad Prabhakar 	struct v4l2_mbus_framefmt *src_format;
20907fc05bdSLad Prabhakar 	struct v4l2_mbus_framefmt *sink_format;
21007fc05bdSLad Prabhakar 
21107fc05bdSLad Prabhakar 	src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE);
21207fc05bdSLad Prabhakar 	if (fmt->pad == RZG2L_CRU_IP_SOURCE) {
213*5f5ed645SLad Prabhakar 		fmt->format = *src_format;
214*5f5ed645SLad Prabhakar 		return 0;
21507fc05bdSLad Prabhakar 	}
21607fc05bdSLad Prabhakar 
21707fc05bdSLad Prabhakar 	sink_format = v4l2_subdev_state_get_format(state, fmt->pad);
21807fc05bdSLad Prabhakar 
2195755be5fSLaurent Pinchart 	if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code))
22007fc05bdSLad Prabhakar 		sink_format->code = rzg2l_cru_ip_formats[0].codes[0];
22107fc05bdSLad Prabhakar 	else
22207fc05bdSLad Prabhakar 		sink_format->code = fmt->format.code;
22307fc05bdSLad Prabhakar 
22407fc05bdSLad Prabhakar 	sink_format->field = V4L2_FIELD_NONE;
22507fc05bdSLad Prabhakar 	sink_format->colorspace = fmt->format.colorspace;
22607fc05bdSLad Prabhakar 	sink_format->xfer_func = fmt->format.xfer_func;
22707fc05bdSLad Prabhakar 	sink_format->ycbcr_enc = fmt->format.ycbcr_enc;
22807fc05bdSLad Prabhakar 	sink_format->quantization = fmt->format.quantization;
22907fc05bdSLad Prabhakar 	sink_format->width = clamp_t(u32, fmt->format.width,
23007fc05bdSLad Prabhakar 				     RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width);
23107fc05bdSLad Prabhakar 	sink_format->height = clamp_t(u32, fmt->format.height,
23207fc05bdSLad Prabhakar 				      RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height);
23307fc05bdSLad Prabhakar 
23407fc05bdSLad Prabhakar 	fmt->format = *sink_format;
23507fc05bdSLad Prabhakar 
23607fc05bdSLad Prabhakar 	/* propagate format to source pad */
23707fc05bdSLad Prabhakar 	*src_format = *sink_format;
23807fc05bdSLad Prabhakar 
23907fc05bdSLad Prabhakar 	return 0;
24007fc05bdSLad Prabhakar }
24107fc05bdSLad Prabhakar 
rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_mbus_code_enum * code)24207fc05bdSLad Prabhakar static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd,
24307fc05bdSLad Prabhakar 				       struct v4l2_subdev_state *state,
24407fc05bdSLad Prabhakar 				       struct v4l2_subdev_mbus_code_enum *code)
24507fc05bdSLad Prabhakar {
24607fc05bdSLad Prabhakar 	unsigned int index = code->index;
24707fc05bdSLad Prabhakar 	unsigned int i, j;
24807fc05bdSLad Prabhakar 
24907fc05bdSLad Prabhakar 	for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
25007fc05bdSLad Prabhakar 		const struct rzg2l_cru_ip_format *fmt = &rzg2l_cru_ip_formats[i];
25107fc05bdSLad Prabhakar 
2525755be5fSLaurent Pinchart 		for (j = 0; j < ARRAY_SIZE(fmt->codes); j++) {
2535755be5fSLaurent Pinchart 			if (!fmt->codes[j])
2545755be5fSLaurent Pinchart 				continue;
2555755be5fSLaurent Pinchart 
25607fc05bdSLad Prabhakar 			if (!index) {
25707fc05bdSLad Prabhakar 				code->code = fmt->codes[j];
25807fc05bdSLad Prabhakar 				return 0;
25907fc05bdSLad Prabhakar 			}
26007fc05bdSLad Prabhakar 
26107fc05bdSLad Prabhakar 			index--;
26207fc05bdSLad Prabhakar 		}
26307fc05bdSLad Prabhakar 	}
26407fc05bdSLad Prabhakar 
26507fc05bdSLad Prabhakar 	return -EINVAL;
26607fc05bdSLad Prabhakar }
2675755be5fSLaurent Pinchart 
rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_frame_size_enum * fse)26807fc05bdSLad Prabhakar static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
26907fc05bdSLad Prabhakar 					struct v4l2_subdev_state *state,
27007fc05bdSLad Prabhakar 					struct v4l2_subdev_frame_size_enum *fse)
27107fc05bdSLad Prabhakar {
27207fc05bdSLad Prabhakar 	struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
27307fc05bdSLad Prabhakar 	const struct rzg2l_cru_info *info = cru->info;
27407fc05bdSLad Prabhakar 
27507fc05bdSLad Prabhakar 	if (fse->index != 0)
276ad982f85SLad Prabhakar 		return -EINVAL;
277ad982f85SLad Prabhakar 
278ad982f85SLad Prabhakar 	if (!rzg2l_cru_ip_code_to_fmt(fse->code))
279ad982f85SLad Prabhakar 		return -EINVAL;
28007fc05bdSLad Prabhakar 
28107fc05bdSLad Prabhakar 	fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH;
28207fc05bdSLad Prabhakar 	fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT;
28307fc05bdSLad Prabhakar 	fse->max_width = info->max_width;
28407fc05bdSLad Prabhakar 	fse->max_height = info->max_height;
28507fc05bdSLad Prabhakar 
28607fc05bdSLad Prabhakar 	return 0;
28707fc05bdSLad Prabhakar }
28807fc05bdSLad Prabhakar 
rzg2l_cru_ip_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)28907fc05bdSLad Prabhakar static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd,
29007fc05bdSLad Prabhakar 				   struct v4l2_subdev_state *sd_state)
29107fc05bdSLad Prabhakar {
29207fc05bdSLad Prabhakar 	struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, };
29307fc05bdSLad Prabhakar 
29407fc05bdSLad Prabhakar 	fmt.format.width = RZG2L_CRU_MIN_INPUT_WIDTH;
29507fc05bdSLad Prabhakar 	fmt.format.height = RZG2L_CRU_MIN_INPUT_HEIGHT;
29607fc05bdSLad Prabhakar 	fmt.format.field = V4L2_FIELD_NONE;
29707fc05bdSLad Prabhakar 	fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
29807fc05bdSLad Prabhakar 	fmt.format.colorspace = V4L2_COLORSPACE_SRGB;
29907fc05bdSLad Prabhakar 	fmt.format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
30007fc05bdSLad Prabhakar 	fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT;
30107fc05bdSLad Prabhakar 	fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT;
30207fc05bdSLad Prabhakar 
30307fc05bdSLad Prabhakar 	return rzg2l_cru_ip_set_format(sd, sd_state, &fmt);
30407fc05bdSLad Prabhakar }
30507fc05bdSLad Prabhakar 
30607fc05bdSLad Prabhakar static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = {
30707fc05bdSLad Prabhakar 	.s_stream = rzg2l_cru_ip_s_stream,
30807fc05bdSLad Prabhakar };
30907fc05bdSLad Prabhakar 
310 static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = {
311 	.enum_mbus_code = rzg2l_cru_ip_enum_mbus_code,
312 	.enum_frame_size = rzg2l_cru_ip_enum_frame_size,
313 	.get_fmt = v4l2_subdev_get_fmt,
314 	.set_fmt = rzg2l_cru_ip_set_format,
315 };
316 
317 static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = {
318 	.video = &rzg2l_cru_ip_video_ops,
319 	.pad = &rzg2l_cru_ip_pad_ops,
320 };
321 
322 static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = {
323 	.init_state = rzg2l_cru_ip_init_state,
324 };
325 
326 static const struct media_entity_operations rzg2l_cru_ip_entity_ops = {
327 	.link_validate = v4l2_subdev_link_validate,
328 };
329 
rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev * cru)330 int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru)
331 {
332 	struct rzg2l_cru_ip *ip = &cru->ip;
333 	int ret;
334 
335 	ip->subdev.dev = cru->dev;
336 	v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops);
337 	ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops;
338 	v4l2_set_subdevdata(&ip->subdev, cru);
339 	snprintf(ip->subdev.name, sizeof(ip->subdev.name),
340 		 "cru-ip-%s", dev_name(cru->dev));
341 	ip->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
342 
343 	ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
344 	ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops;
345 
346 	ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK |
347 					    MEDIA_PAD_FL_MUST_CONNECT;
348 	ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
349 					      MEDIA_PAD_FL_MUST_CONNECT;
350 
351 	ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads);
352 	if (ret)
353 		return ret;
354 
355 	ret = v4l2_subdev_init_finalize(&ip->subdev);
356 	if (ret < 0)
357 		goto entity_cleanup;
358 
359 	ret = v4l2_device_register_subdev(&cru->v4l2_dev, &ip->subdev);
360 	if (ret < 0)
361 		goto error_subdev;
362 
363 	return 0;
364 error_subdev:
365 	v4l2_subdev_cleanup(&ip->subdev);
366 entity_cleanup:
367 	media_entity_cleanup(&ip->subdev.entity);
368 
369 	return ret;
370 }
371 
rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev * cru)372 void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru)
373 {
374 	struct rzg2l_cru_ip *ip = &cru->ip;
375 
376 	media_entity_cleanup(&ip->subdev.entity);
377 	v4l2_subdev_cleanup(&ip->subdev);
378 	v4l2_device_unregister_subdev(&ip->subdev);
379 }
380