xref: /linux/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c (revision 68a052239fc4b351e961f698b824f7654a346091)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013--2024 Intel Corporation
4  */
5 
6 #include <linux/bug.h>
7 #include <linux/device.h>
8 #include <linux/minmax.h>
9 
10 #include <media/media-entity.h>
11 #include <media/mipi-csi2.h>
12 #include <media/v4l2-ctrls.h>
13 #include <media/v4l2-subdev.h>
14 
15 #include "ipu6-bus.h"
16 #include "ipu6-isys.h"
17 #include "ipu6-isys-subdev.h"
18 
19 unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
20 {
21 	switch (code) {
22 	case MEDIA_BUS_FMT_RGB888_1X24:
23 	case MEDIA_BUS_FMT_META_24:
24 		return 24;
25 	case MEDIA_BUS_FMT_RGB565_1X16:
26 	case MEDIA_BUS_FMT_UYVY8_1X16:
27 	case MEDIA_BUS_FMT_YUYV8_1X16:
28 	case MEDIA_BUS_FMT_META_16:
29 		return 16;
30 	case MEDIA_BUS_FMT_SBGGR12_1X12:
31 	case MEDIA_BUS_FMT_SGBRG12_1X12:
32 	case MEDIA_BUS_FMT_SGRBG12_1X12:
33 	case MEDIA_BUS_FMT_SRGGB12_1X12:
34 	case MEDIA_BUS_FMT_META_12:
35 		return 12;
36 	case MEDIA_BUS_FMT_SBGGR10_1X10:
37 	case MEDIA_BUS_FMT_SGBRG10_1X10:
38 	case MEDIA_BUS_FMT_SGRBG10_1X10:
39 	case MEDIA_BUS_FMT_SRGGB10_1X10:
40 	case MEDIA_BUS_FMT_META_10:
41 		return 10;
42 	case MEDIA_BUS_FMT_SBGGR8_1X8:
43 	case MEDIA_BUS_FMT_SGBRG8_1X8:
44 	case MEDIA_BUS_FMT_SGRBG8_1X8:
45 	case MEDIA_BUS_FMT_SRGGB8_1X8:
46 	case MEDIA_BUS_FMT_META_8:
47 		return 8;
48 	default:
49 		WARN_ON(1);
50 		return 8;
51 	}
52 }
53 
54 unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
55 {
56 	switch (code) {
57 	case MEDIA_BUS_FMT_RGB565_1X16:
58 		return MIPI_CSI2_DT_RGB565;
59 	case MEDIA_BUS_FMT_RGB888_1X24:
60 		return MIPI_CSI2_DT_RGB888;
61 	case MEDIA_BUS_FMT_UYVY8_1X16:
62 	case MEDIA_BUS_FMT_YUYV8_1X16:
63 		return MIPI_CSI2_DT_YUV422_8B;
64 	case MEDIA_BUS_FMT_SBGGR16_1X16:
65 	case MEDIA_BUS_FMT_SGBRG16_1X16:
66 	case MEDIA_BUS_FMT_SGRBG16_1X16:
67 	case MEDIA_BUS_FMT_SRGGB16_1X16:
68 		return MIPI_CSI2_DT_RAW16;
69 	case MEDIA_BUS_FMT_SBGGR12_1X12:
70 	case MEDIA_BUS_FMT_SGBRG12_1X12:
71 	case MEDIA_BUS_FMT_SGRBG12_1X12:
72 	case MEDIA_BUS_FMT_SRGGB12_1X12:
73 		return MIPI_CSI2_DT_RAW12;
74 	case MEDIA_BUS_FMT_SBGGR10_1X10:
75 	case MEDIA_BUS_FMT_SGBRG10_1X10:
76 	case MEDIA_BUS_FMT_SGRBG10_1X10:
77 	case MEDIA_BUS_FMT_SRGGB10_1X10:
78 		return MIPI_CSI2_DT_RAW10;
79 	case MEDIA_BUS_FMT_SBGGR8_1X8:
80 	case MEDIA_BUS_FMT_SGBRG8_1X8:
81 	case MEDIA_BUS_FMT_SGRBG8_1X8:
82 	case MEDIA_BUS_FMT_SRGGB8_1X8:
83 		return MIPI_CSI2_DT_RAW8;
84 	case MEDIA_BUS_FMT_META_8:
85 	case MEDIA_BUS_FMT_META_10:
86 	case MEDIA_BUS_FMT_META_12:
87 	case MEDIA_BUS_FMT_META_16:
88 	case MEDIA_BUS_FMT_META_24:
89 		return MIPI_CSI2_DT_EMBEDDED_8B;
90 	default:
91 		/* return unavailable MIPI data type - 0x3f */
92 		WARN_ON(1);
93 		return 0x3f;
94 	}
95 }
96 
97 bool ipu6_isys_is_bayer_format(u32 code)
98 {
99 	switch (ipu6_isys_mbus_code_to_mipi(code)) {
100 	case MIPI_CSI2_DT_RAW8:
101 	case MIPI_CSI2_DT_RAW10:
102 	case MIPI_CSI2_DT_RAW12:
103 	case MIPI_CSI2_DT_RAW14:
104 	case MIPI_CSI2_DT_RAW16:
105 	case MIPI_CSI2_DT_RAW20:
106 	case MIPI_CSI2_DT_RAW24:
107 	case MIPI_CSI2_DT_RAW28:
108 		return true;
109 	default:
110 		return false;
111 	}
112 }
113 
114 u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
115 {
116 	static const u32 code_map[] = {
117 		MEDIA_BUS_FMT_SRGGB8_1X8,
118 		MEDIA_BUS_FMT_SGRBG8_1X8,
119 		MEDIA_BUS_FMT_SGBRG8_1X8,
120 		MEDIA_BUS_FMT_SBGGR8_1X8,
121 		MEDIA_BUS_FMT_SRGGB10_1X10,
122 		MEDIA_BUS_FMT_SGRBG10_1X10,
123 		MEDIA_BUS_FMT_SGBRG10_1X10,
124 		MEDIA_BUS_FMT_SBGGR10_1X10,
125 		MEDIA_BUS_FMT_SRGGB12_1X12,
126 		MEDIA_BUS_FMT_SGRBG12_1X12,
127 		MEDIA_BUS_FMT_SGBRG12_1X12,
128 		MEDIA_BUS_FMT_SBGGR12_1X12,
129 		MEDIA_BUS_FMT_SRGGB16_1X16,
130 		MEDIA_BUS_FMT_SGRBG16_1X16,
131 		MEDIA_BUS_FMT_SGBRG16_1X16,
132 		MEDIA_BUS_FMT_SBGGR16_1X16,
133 	};
134 	u32 i;
135 
136 	for (i = 0; i < ARRAY_SIZE(code_map); i++)
137 		if (code_map[i] == code)
138 			break;
139 
140 	if (WARN_ON(i == ARRAY_SIZE(code_map)))
141 		return code;
142 
143 	return code_map[i ^ (((y & 1) << 1) | (x & 1))];
144 }
145 
146 int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
147 			     struct v4l2_subdev_state *state,
148 			     struct v4l2_subdev_format *format)
149 {
150 	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
151 	struct v4l2_mbus_framefmt *fmt;
152 	struct v4l2_rect *crop;
153 	u32 code = asd->supported_codes[0];
154 	u32 other_pad, other_stream;
155 	unsigned int i;
156 	int ret;
157 
158 	/* No transcoding, source and sink formats must match. */
159 	if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
160 	    sd->entity.num_pads > 1)
161 		return v4l2_subdev_get_fmt(sd, state, format);
162 
163 	format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,
164 				     IPU6_ISYS_MAX_WIDTH);
165 	format->format.height = clamp(format->format.height,
166 				      IPU6_ISYS_MIN_HEIGHT,
167 				      IPU6_ISYS_MAX_HEIGHT);
168 
169 	for (i = 0; asd->supported_codes[i]; i++) {
170 		if (asd->supported_codes[i] == format->format.code) {
171 			code = asd->supported_codes[i];
172 			break;
173 		}
174 	}
175 	format->format.code = code;
176 	format->format.field = V4L2_FIELD_NONE;
177 
178 	/* Store the format and propagate it to the source pad. */
179 	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
180 	if (!fmt)
181 		return -EINVAL;
182 
183 	*fmt = format->format;
184 
185 	if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
186 		return 0;
187 
188 	/* propagate format to following source pad */
189 	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
190 							   format->stream);
191 	if (!fmt)
192 		return -EINVAL;
193 
194 	*fmt = format->format;
195 
196 	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
197 						    format->pad,
198 						    format->stream,
199 						    &other_pad,
200 						    &other_stream);
201 	if (ret)
202 		return -EINVAL;
203 
204 	crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
205 	/* reset crop */
206 	crop->left = 0;
207 	crop->top = 0;
208 	crop->width = fmt->width;
209 	crop->height = fmt->height;
210 
211 	return 0;
212 }
213 
214 int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
215 				    struct v4l2_subdev_state *state,
216 				    struct v4l2_subdev_mbus_code_enum *code)
217 {
218 	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
219 	const u32 *supported_codes = asd->supported_codes;
220 	u32 index;
221 
222 	for (index = 0; supported_codes[index]; index++) {
223 		if (index == code->index) {
224 			code->code = supported_codes[index];
225 			return 0;
226 		}
227 	}
228 
229 	return -EINVAL;
230 }
231 
232 static int subdev_set_routing(struct v4l2_subdev *sd,
233 			      struct v4l2_subdev_state *state,
234 			      struct v4l2_subdev_krouting *routing)
235 {
236 	static const struct v4l2_mbus_framefmt format = {
237 		.width = 4096,
238 		.height = 3072,
239 		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
240 		.field = V4L2_FIELD_NONE,
241 	};
242 	int ret;
243 
244 	ret = v4l2_subdev_routing_validate(sd, routing,
245 					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
246 	if (ret)
247 		return ret;
248 
249 	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
250 }
251 
252 int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
253 				 struct v4l2_mbus_framefmt *format)
254 {
255 	struct v4l2_mbus_framefmt *fmt;
256 	struct v4l2_subdev_state *state;
257 
258 	if (!sd || !format)
259 		return -EINVAL;
260 
261 	state = v4l2_subdev_lock_and_get_active_state(sd);
262 	fmt = v4l2_subdev_state_get_format(state, pad, stream);
263 	if (fmt)
264 		*format = *fmt;
265 	v4l2_subdev_unlock_state(state);
266 
267 	return fmt ? 0 : -EINVAL;
268 }
269 
270 int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
271 				  struct v4l2_rect *crop)
272 {
273 	struct v4l2_subdev_state *state;
274 	struct v4l2_rect *rect;
275 
276 	if (!sd || !crop)
277 		return -EINVAL;
278 
279 	state = v4l2_subdev_lock_and_get_active_state(sd);
280 	rect = v4l2_subdev_state_get_crop(state, pad, stream);
281 	if (rect)
282 		*crop = *rect;
283 	v4l2_subdev_unlock_state(state);
284 
285 	return rect ? 0 : -EINVAL;
286 }
287 
288 u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
289 {
290 	struct v4l2_subdev_state *state;
291 	struct v4l2_subdev_route *routes;
292 	unsigned int i;
293 	u32 source_stream = 0;
294 
295 	state = v4l2_subdev_lock_and_get_active_state(sd);
296 	if (!state)
297 		return 0;
298 
299 	routes = state->routing.routes;
300 	for (i = 0; i < state->routing.num_routes; i++) {
301 		if (routes[i].source_pad == pad) {
302 			source_stream = routes[i].source_stream;
303 			break;
304 		}
305 	}
306 
307 	v4l2_subdev_unlock_state(state);
308 
309 	return source_stream;
310 }
311 
312 static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,
313 				       struct v4l2_subdev_state *state)
314 {
315 	struct v4l2_subdev_route route = {
316 		.sink_pad = 0,
317 		.sink_stream = 0,
318 		.source_pad = 1,
319 		.source_stream = 0,
320 		.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
321 	};
322 	struct v4l2_subdev_krouting routing = {
323 		.num_routes = 1,
324 		.routes = &route,
325 	};
326 
327 	return subdev_set_routing(sd, state, &routing);
328 }
329 
330 int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
331 				 struct v4l2_subdev_state *state,
332 				 enum v4l2_subdev_format_whence which,
333 				 struct v4l2_subdev_krouting *routing)
334 {
335 	return subdev_set_routing(sd, state, routing);
336 }
337 
338 static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {
339 	.init_state = ipu6_isys_subdev_init_state,
340 };
341 
342 int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
343 			  const struct v4l2_subdev_ops *ops,
344 			  unsigned int nr_ctrls,
345 			  unsigned int num_sink_pads,
346 			  unsigned int num_source_pads)
347 {
348 	unsigned int num_pads = num_sink_pads + num_source_pads;
349 	unsigned int i;
350 	int ret;
351 
352 	v4l2_subdev_init(&asd->sd, ops);
353 
354 	asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
355 			 V4L2_SUBDEV_FL_HAS_EVENTS |
356 			 V4L2_SUBDEV_FL_STREAMS;
357 	asd->sd.owner = THIS_MODULE;
358 	asd->sd.dev = &asd->isys->adev->auxdev.dev;
359 	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
360 	asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;
361 
362 	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
363 				sizeof(*asd->pad), GFP_KERNEL);
364 	if (!asd->pad)
365 		return -ENOMEM;
366 
367 	for (i = 0; i < num_sink_pads; i++)
368 		asd->pad[i].flags = MEDIA_PAD_FL_SINK |
369 				    MEDIA_PAD_FL_MUST_CONNECT;
370 
371 	for (i = num_sink_pads; i < num_pads; i++)
372 		asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
373 
374 	ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
375 	if (ret)
376 		return ret;
377 
378 	if (asd->ctrl_init) {
379 		ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
380 		if (ret)
381 			goto out_media_entity_cleanup;
382 
383 		asd->ctrl_init(&asd->sd);
384 		if (asd->ctrl_handler.error) {
385 			ret = asd->ctrl_handler.error;
386 			goto out_v4l2_ctrl_handler_free;
387 		}
388 
389 		asd->sd.ctrl_handler = &asd->ctrl_handler;
390 	}
391 
392 	asd->source = -1;
393 
394 	return 0;
395 
396 out_v4l2_ctrl_handler_free:
397 	v4l2_ctrl_handler_free(&asd->ctrl_handler);
398 
399 out_media_entity_cleanup:
400 	media_entity_cleanup(&asd->sd.entity);
401 
402 	return ret;
403 }
404 
405 void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)
406 {
407 	media_entity_cleanup(&asd->sd.entity);
408 	v4l2_ctrl_handler_free(&asd->ctrl_handler);
409 }
410