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