xref: /linux/drivers/media/platform/microchip/microchip-isc-scaler.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Microchip Image Sensor Controller (ISC) Scaler entity support
4  *
5  * Copyright (C) 2022 Microchip Technology, Inc.
6  *
7  * Author: Eugen Hristev <eugen.hristev@microchip.com>
8  *
9  */
10 
11 #include <media/media-device.h>
12 #include <media/media-entity.h>
13 #include <media/v4l2-device.h>
14 #include <media/v4l2-subdev.h>
15 
16 #include "microchip-isc-regs.h"
17 #include "microchip-isc.h"
18 
19 static void isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt *framefmt)
20 {
21 	framefmt->colorspace = V4L2_COLORSPACE_SRGB;
22 	framefmt->field = V4L2_FIELD_NONE;
23 	framefmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
24 	framefmt->quantization = V4L2_QUANTIZATION_DEFAULT;
25 	framefmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
26 };
27 
28 static int isc_scaler_get_fmt(struct v4l2_subdev *sd,
29 			      struct v4l2_subdev_state *sd_state,
30 			      struct v4l2_subdev_format *format)
31 {
32 	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
33 	struct v4l2_mbus_framefmt *v4l2_try_fmt;
34 
35 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
36 		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
37 							    format->pad);
38 		format->format = *v4l2_try_fmt;
39 
40 		return 0;
41 	}
42 
43 	format->format = isc->scaler_format[format->pad];
44 
45 	return 0;
46 }
47 
48 static int isc_scaler_set_fmt(struct v4l2_subdev *sd,
49 			      struct v4l2_subdev_state *sd_state,
50 			      struct v4l2_subdev_format *req_fmt)
51 {
52 	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
53 	struct v4l2_mbus_framefmt *v4l2_try_fmt;
54 	struct isc_format *fmt;
55 	unsigned int i;
56 
57 	/* Source format is fixed, we cannot change it */
58 	if (req_fmt->pad == ISC_SCALER_PAD_SOURCE) {
59 		req_fmt->format = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
60 		return 0;
61 	}
62 
63 	/* There is no limit on the frame size on the sink pad */
64 	v4l_bound_align_image(&req_fmt->format.width, 16, UINT_MAX, 0,
65 			      &req_fmt->format.height, 16, UINT_MAX, 0, 0);
66 
67 	isc_scaler_prepare_fmt(&req_fmt->format);
68 
69 	fmt = isc_find_format_by_code(isc, req_fmt->format.code, &i);
70 
71 	if (!fmt)
72 		fmt = &isc->formats_list[0];
73 
74 	req_fmt->format.code = fmt->mbus_code;
75 
76 	if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
77 		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
78 							    req_fmt->pad);
79 		*v4l2_try_fmt = req_fmt->format;
80 		/* Trying on the sink pad makes the source pad change too */
81 		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
82 							    ISC_SCALER_PAD_SOURCE);
83 		*v4l2_try_fmt = req_fmt->format;
84 
85 		v4l_bound_align_image(&v4l2_try_fmt->width,
86 				      16, isc->max_width, 0,
87 				      &v4l2_try_fmt->height,
88 				      16, isc->max_height, 0, 0);
89 		/* if we are just trying, we are done */
90 		return 0;
91 	}
92 
93 	isc->scaler_format[ISC_SCALER_PAD_SINK] = req_fmt->format;
94 
95 	/* The source pad is the same as the sink, but we have to crop it */
96 	isc->scaler_format[ISC_SCALER_PAD_SOURCE] =
97 		isc->scaler_format[ISC_SCALER_PAD_SINK];
98 	v4l_bound_align_image
99 		(&isc->scaler_format[ISC_SCALER_PAD_SOURCE].width, 16,
100 		 isc->max_width, 0,
101 		 &isc->scaler_format[ISC_SCALER_PAD_SOURCE].height, 16,
102 		 isc->max_height, 0, 0);
103 
104 	return 0;
105 }
106 
107 static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd,
108 				     struct v4l2_subdev_state *sd_state,
109 				     struct v4l2_subdev_mbus_code_enum *code)
110 {
111 	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
112 
113 	/*
114 	 * All formats supported by the ISC are supported by the scaler.
115 	 * Advertise the formats which the ISC can take as input, as the scaler
116 	 * entity cropping is part of the PFE module (parallel front end)
117 	 */
118 	if (code->index < isc->formats_list_size) {
119 		code->code = isc->formats_list[code->index].mbus_code;
120 		return 0;
121 	}
122 
123 	return -EINVAL;
124 }
125 
126 static int isc_scaler_g_sel(struct v4l2_subdev *sd,
127 			    struct v4l2_subdev_state *sd_state,
128 			    struct v4l2_subdev_selection *sel)
129 {
130 	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
131 
132 	if (sel->pad == ISC_SCALER_PAD_SOURCE)
133 		return -EINVAL;
134 
135 	if (sel->target != V4L2_SEL_TGT_CROP_BOUNDS &&
136 	    sel->target != V4L2_SEL_TGT_CROP)
137 		return -EINVAL;
138 
139 	sel->r.height = isc->scaler_format[ISC_SCALER_PAD_SOURCE].height;
140 	sel->r.width = isc->scaler_format[ISC_SCALER_PAD_SOURCE].width;
141 
142 	sel->r.left = 0;
143 	sel->r.top = 0;
144 
145 	return 0;
146 }
147 
148 static int isc_scaler_init_state(struct v4l2_subdev *sd,
149 				 struct v4l2_subdev_state *sd_state)
150 {
151 	struct v4l2_mbus_framefmt *v4l2_try_fmt =
152 		v4l2_subdev_state_get_format(sd_state, 0);
153 	struct v4l2_rect *try_crop;
154 	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
155 
156 	*v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
157 
158 	try_crop = v4l2_subdev_state_get_crop(sd_state, 0);
159 
160 	try_crop->top = 0;
161 	try_crop->left = 0;
162 	try_crop->width = v4l2_try_fmt->width;
163 	try_crop->height = v4l2_try_fmt->height;
164 
165 	return 0;
166 }
167 
168 static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = {
169 	.enum_mbus_code = isc_scaler_enum_mbus_code,
170 	.set_fmt = isc_scaler_set_fmt,
171 	.get_fmt = isc_scaler_get_fmt,
172 	.get_selection = isc_scaler_g_sel,
173 };
174 
175 static const struct media_entity_operations isc_scaler_entity_ops = {
176 	.link_validate = v4l2_subdev_link_validate,
177 };
178 
179 static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = {
180 	.pad = &isc_scaler_pad_ops,
181 };
182 
183 static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = {
184 	.init_state = isc_scaler_init_state,
185 };
186 
187 int isc_scaler_init(struct isc_device *isc)
188 {
189 	int ret;
190 
191 	v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops);
192 	isc->scaler_sd.internal_ops = &isc_scaler_internal_ops;
193 
194 	isc->scaler_sd.owner = THIS_MODULE;
195 	isc->scaler_sd.dev = isc->dev;
196 	snprintf(isc->scaler_sd.name, sizeof(isc->scaler_sd.name),
197 		 "microchip_isc_scaler");
198 
199 	isc->scaler_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
200 	isc->scaler_sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
201 	isc->scaler_sd.entity.ops = &isc_scaler_entity_ops;
202 	isc->scaler_pads[ISC_SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
203 	isc->scaler_pads[ISC_SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
204 
205 	isc_scaler_prepare_fmt(&isc->scaler_format[ISC_SCALER_PAD_SOURCE]);
206 	isc->scaler_format[ISC_SCALER_PAD_SOURCE].height = isc->max_height;
207 	isc->scaler_format[ISC_SCALER_PAD_SOURCE].width = isc->max_width;
208 	isc->scaler_format[ISC_SCALER_PAD_SOURCE].code =
209 		 isc->formats_list[0].mbus_code;
210 
211 	isc->scaler_format[ISC_SCALER_PAD_SINK] =
212 		 isc->scaler_format[ISC_SCALER_PAD_SOURCE];
213 
214 	ret = media_entity_pads_init(&isc->scaler_sd.entity,
215 				     ISC_SCALER_PADS_NUM,
216 				     isc->scaler_pads);
217 	if (ret < 0) {
218 		dev_err(isc->dev, "scaler sd media entity init failed\n");
219 		return ret;
220 	}
221 
222 	ret = v4l2_device_register_subdev(&isc->v4l2_dev, &isc->scaler_sd);
223 	if (ret < 0) {
224 		dev_err(isc->dev, "scaler sd failed to register subdev\n");
225 		return ret;
226 	}
227 
228 	return ret;
229 }
230 EXPORT_SYMBOL_GPL(isc_scaler_init);
231 
232 int isc_scaler_link(struct isc_device *isc)
233 {
234 	int ret;
235 
236 	ret = media_create_pad_link(&isc->current_subdev->sd->entity,
237 				    isc->remote_pad, &isc->scaler_sd.entity,
238 				    ISC_SCALER_PAD_SINK,
239 				    MEDIA_LNK_FL_ENABLED |
240 				    MEDIA_LNK_FL_IMMUTABLE);
241 
242 	if (ret < 0) {
243 		dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
244 			isc->current_subdev->sd->entity.name,
245 			isc->scaler_sd.entity.name);
246 		return ret;
247 	}
248 
249 	dev_dbg(isc->dev, "link with %s pad: %d\n",
250 		isc->current_subdev->sd->name, isc->remote_pad);
251 
252 	ret = media_create_pad_link(&isc->scaler_sd.entity,
253 				    ISC_SCALER_PAD_SOURCE,
254 				    &isc->video_dev.entity, ISC_PAD_SINK,
255 				    MEDIA_LNK_FL_ENABLED |
256 				    MEDIA_LNK_FL_IMMUTABLE);
257 
258 	if (ret < 0) {
259 		dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
260 			isc->scaler_sd.entity.name,
261 			isc->video_dev.entity.name);
262 		return ret;
263 	}
264 
265 	dev_dbg(isc->dev, "link with %s pad: %d\n", isc->scaler_sd.name,
266 		ISC_SCALER_PAD_SOURCE);
267 
268 	return ret;
269 }
270 EXPORT_SYMBOL_GPL(isc_scaler_link);
271 
272