1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2013 - 2025 Intel Corporation
4 */
5
6 #include <linux/bug.h>
7 #include <linux/device.h>
8 #include <linux/minmax.h>
9 #include <linux/types.h>
10
11 #include <media/media-entity.h>
12 #include <media/mipi-csi2.h>
13 #include <media/v4l2-ctrls.h>
14 #include <media/v4l2-subdev.h>
15
16 #include <uapi/linux/media-bus-format.h>
17
18 #include "ipu7-bus.h"
19 #include "ipu7-isys.h"
20 #include "ipu7-isys-subdev.h"
21
ipu7_isys_mbus_code_to_mipi(u32 code)22 unsigned int ipu7_isys_mbus_code_to_mipi(u32 code)
23 {
24 switch (code) {
25 case MEDIA_BUS_FMT_RGB565_1X16:
26 return MIPI_CSI2_DT_RGB565;
27 case MEDIA_BUS_FMT_RGB888_1X24:
28 return MIPI_CSI2_DT_RGB888;
29 case MEDIA_BUS_FMT_YUYV10_1X20:
30 return MIPI_CSI2_DT_YUV422_10B;
31 case MEDIA_BUS_FMT_UYVY8_1X16:
32 case MEDIA_BUS_FMT_YUYV8_1X16:
33 return MIPI_CSI2_DT_YUV422_8B;
34 case MEDIA_BUS_FMT_SBGGR12_1X12:
35 case MEDIA_BUS_FMT_SGBRG12_1X12:
36 case MEDIA_BUS_FMT_SGRBG12_1X12:
37 case MEDIA_BUS_FMT_SRGGB12_1X12:
38 return MIPI_CSI2_DT_RAW12;
39 case MEDIA_BUS_FMT_Y10_1X10:
40 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 return MIPI_CSI2_DT_RAW10;
45 case MEDIA_BUS_FMT_SBGGR8_1X8:
46 case MEDIA_BUS_FMT_SGBRG8_1X8:
47 case MEDIA_BUS_FMT_SGRBG8_1X8:
48 case MEDIA_BUS_FMT_SRGGB8_1X8:
49 return MIPI_CSI2_DT_RAW8;
50 default:
51 WARN_ON(1);
52 return 0xff;
53 }
54 }
55
ipu7_isys_is_bayer_format(u32 code)56 bool ipu7_isys_is_bayer_format(u32 code)
57 {
58 switch (ipu7_isys_mbus_code_to_mipi(code)) {
59 case MIPI_CSI2_DT_RAW8:
60 case MIPI_CSI2_DT_RAW10:
61 case MIPI_CSI2_DT_RAW12:
62 case MIPI_CSI2_DT_RAW14:
63 case MIPI_CSI2_DT_RAW16:
64 case MIPI_CSI2_DT_RAW20:
65 case MIPI_CSI2_DT_RAW24:
66 case MIPI_CSI2_DT_RAW28:
67 return true;
68 default:
69 return false;
70 }
71 }
72
ipu7_isys_convert_bayer_order(u32 code,int x,int y)73 u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y)
74 {
75 static const u32 code_map[] = {
76 MEDIA_BUS_FMT_SRGGB8_1X8,
77 MEDIA_BUS_FMT_SGRBG8_1X8,
78 MEDIA_BUS_FMT_SGBRG8_1X8,
79 MEDIA_BUS_FMT_SBGGR8_1X8,
80 MEDIA_BUS_FMT_SRGGB10_1X10,
81 MEDIA_BUS_FMT_SGRBG10_1X10,
82 MEDIA_BUS_FMT_SGBRG10_1X10,
83 MEDIA_BUS_FMT_SBGGR10_1X10,
84 MEDIA_BUS_FMT_SRGGB12_1X12,
85 MEDIA_BUS_FMT_SGRBG12_1X12,
86 MEDIA_BUS_FMT_SGBRG12_1X12,
87 MEDIA_BUS_FMT_SBGGR12_1X12,
88 };
89 unsigned int i;
90
91 for (i = 0; i < ARRAY_SIZE(code_map); i++)
92 if (code_map[i] == code)
93 break;
94
95 if (WARN_ON(i == ARRAY_SIZE(code_map)))
96 return code;
97
98 return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))];
99 }
100
ipu7_isys_subdev_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_format * format)101 int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
102 struct v4l2_subdev_state *state,
103 struct v4l2_subdev_format *format)
104 {
105 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
106 u32 code = asd->supported_codes[0];
107 struct v4l2_mbus_framefmt *fmt;
108 u32 other_pad, other_stream;
109 struct v4l2_rect *crop;
110 unsigned int i;
111 int ret;
112
113 /* No transcoding, source and sink formats must match. */
114 if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
115 sd->entity.num_pads > 1)
116 return v4l2_subdev_get_fmt(sd, state, format);
117
118 format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH,
119 IPU_ISYS_MAX_WIDTH);
120 format->format.height = clamp(format->format.height,
121 IPU_ISYS_MIN_HEIGHT,
122 IPU_ISYS_MAX_HEIGHT);
123
124 for (i = 0; asd->supported_codes[i]; i++) {
125 if (asd->supported_codes[i] == format->format.code) {
126 code = asd->supported_codes[i];
127 break;
128 }
129 }
130 format->format.code = code;
131 format->format.field = V4L2_FIELD_NONE;
132
133 /* Store the format and propagate it to the source pad. */
134 fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
135 if (!fmt)
136 return -EINVAL;
137
138 *fmt = format->format;
139
140 if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
141 return 0;
142
143 /* propagate format to following source pad */
144 fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
145 format->stream);
146 if (!fmt)
147 return -EINVAL;
148
149 *fmt = format->format;
150
151 ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
152 format->pad,
153 format->stream,
154 &other_pad,
155 &other_stream);
156 if (ret)
157 return -EINVAL;
158
159 crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
160 /* reset crop */
161 crop->left = 0;
162 crop->top = 0;
163 crop->width = fmt->width;
164 crop->height = fmt->height;
165
166 return 0;
167 }
168
ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_mbus_code_enum * code)169 int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
170 struct v4l2_subdev_state *state,
171 struct v4l2_subdev_mbus_code_enum *code)
172 {
173 struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
174 const u32 *supported_codes = asd->supported_codes;
175 u32 index;
176
177 for (index = 0; supported_codes[index]; index++) {
178 if (index == code->index) {
179 code->code = supported_codes[index];
180 return 0;
181 }
182 }
183
184 return -EINVAL;
185 }
186
subdev_set_routing(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_krouting * routing)187 static int subdev_set_routing(struct v4l2_subdev *sd,
188 struct v4l2_subdev_state *state,
189 struct v4l2_subdev_krouting *routing)
190 {
191 static const struct v4l2_mbus_framefmt fmt = {
192 .width = 4096,
193 .height = 3072,
194 .code = MEDIA_BUS_FMT_SGRBG10_1X10,
195 .field = V4L2_FIELD_NONE,
196 };
197 int ret;
198
199 ret = v4l2_subdev_routing_validate(sd, routing,
200 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
201 if (ret)
202 return ret;
203
204 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
205 }
206
ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev * sd,u32 pad,u32 stream,struct v4l2_mbus_framefmt * format)207 int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
208 struct v4l2_mbus_framefmt *format)
209 {
210 struct v4l2_subdev_state *state;
211 struct v4l2_mbus_framefmt *fmt;
212
213 if (!sd || !format)
214 return -EINVAL;
215
216 state = v4l2_subdev_lock_and_get_active_state(sd);
217 fmt = v4l2_subdev_state_get_format(state, pad, stream);
218 if (fmt)
219 *format = *fmt;
220 v4l2_subdev_unlock_state(state);
221
222 return fmt ? 0 : -EINVAL;
223 }
224
ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev * sd,u32 pad)225 u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
226 {
227 struct v4l2_subdev_state *state;
228 struct v4l2_subdev_route *routes;
229 u32 source_stream = 0;
230 unsigned int i;
231
232 state = v4l2_subdev_lock_and_get_active_state(sd);
233 if (!state)
234 return 0;
235
236 routes = state->routing.routes;
237 for (i = 0; i < state->routing.num_routes; i++) {
238 if (routes[i].source_pad == pad) {
239 source_stream = routes[i].source_stream;
240 break;
241 }
242 }
243
244 v4l2_subdev_unlock_state(state);
245
246 return source_stream;
247 }
248
ipu7_isys_subdev_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * state)249 static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd,
250 struct v4l2_subdev_state *state)
251 {
252 struct v4l2_subdev_route route = {
253 .sink_pad = 0,
254 .sink_stream = 0,
255 .source_pad = 1,
256 .source_stream = 0,
257 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
258 };
259 struct v4l2_subdev_krouting routing = {
260 .num_routes = 1,
261 .routes = &route,
262 };
263
264 return subdev_set_routing(sd, state, &routing);
265 }
266
ipu7_isys_subdev_set_routing(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,enum v4l2_subdev_format_whence which,struct v4l2_subdev_krouting * routing)267 int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
268 struct v4l2_subdev_state *state,
269 enum v4l2_subdev_format_whence which,
270 struct v4l2_subdev_krouting *routing)
271 {
272 return subdev_set_routing(sd, state, routing);
273 }
274
275 static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = {
276 .init_state = ipu7_isys_subdev_init_state,
277 };
278
ipu7_isys_subdev_init(struct ipu7_isys_subdev * asd,const struct v4l2_subdev_ops * ops,unsigned int nr_ctrls,unsigned int num_sink_pads,unsigned int num_source_pads)279 int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
280 const struct v4l2_subdev_ops *ops,
281 unsigned int nr_ctrls,
282 unsigned int num_sink_pads,
283 unsigned int num_source_pads)
284 {
285 unsigned int num_pads = num_sink_pads + num_source_pads;
286 unsigned int i;
287 int ret;
288
289 v4l2_subdev_init(&asd->sd, ops);
290
291 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
292 V4L2_SUBDEV_FL_HAS_EVENTS |
293 V4L2_SUBDEV_FL_STREAMS;
294 asd->sd.owner = THIS_MODULE;
295 asd->sd.dev = &asd->isys->adev->auxdev.dev;
296 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
297 asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops;
298
299 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
300 sizeof(*asd->pad), GFP_KERNEL);
301 if (!asd->pad)
302 return -ENOMEM;
303
304 for (i = 0; i < num_sink_pads; i++)
305 asd->pad[i].flags = MEDIA_PAD_FL_SINK |
306 MEDIA_PAD_FL_MUST_CONNECT;
307
308 for (i = num_sink_pads; i < num_pads; i++)
309 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
310
311 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
312 if (ret) {
313 pr_err("isys subdev init failed %d.\n", ret);
314 return ret;
315 }
316
317 if (asd->ctrl_init) {
318 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
319 if (ret)
320 goto out_media_entity_cleanup;
321
322 asd->ctrl_init(&asd->sd);
323 if (asd->ctrl_handler.error) {
324 ret = asd->ctrl_handler.error;
325 goto out_v4l2_ctrl_handler_free;
326 }
327
328 asd->sd.ctrl_handler = &asd->ctrl_handler;
329 }
330
331 asd->source = -1;
332
333 return 0;
334
335 out_v4l2_ctrl_handler_free:
336 v4l2_ctrl_handler_free(&asd->ctrl_handler);
337
338 out_media_entity_cleanup:
339 media_entity_cleanup(&asd->sd.entity);
340
341 return ret;
342 }
343
ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev * asd)344 void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd)
345 {
346 media_entity_cleanup(&asd->sd.entity);
347 v4l2_ctrl_handler_free(&asd->ctrl_handler);
348 }
349