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