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