xref: /linux/drivers/staging/media/ipu7/ipu7-isys-subdev.c (revision 2ace52718376fdb56aca863da2eebe70d7e2ddb1)
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 	struct v4l2_subdev_route *route;
198 	int ret;
199 
200 	ret = v4l2_subdev_routing_validate(sd, routing,
201 					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
202 					   V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
203 	if (ret)
204 		return ret;
205 
206 	/*
207 	 * The device doesn't support source multiplexing, set all source
208 	 * streams to 0 to simplify stream handling through the driver.
209 	 */
210 	for_each_active_route(routing, route)
211 		route->source_stream = 0;
212 
213 	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
214 }
215 
ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev * sd,u32 pad,u32 stream,struct v4l2_mbus_framefmt * format)216 int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
217 				 struct v4l2_mbus_framefmt *format)
218 {
219 	struct v4l2_subdev_state *state;
220 	struct v4l2_mbus_framefmt *fmt;
221 
222 	if (!sd || !format)
223 		return -EINVAL;
224 
225 	state = v4l2_subdev_lock_and_get_active_state(sd);
226 	fmt = v4l2_subdev_state_get_format(state, pad, stream);
227 	if (fmt)
228 		*format = *fmt;
229 	v4l2_subdev_unlock_state(state);
230 
231 	return fmt ? 0 : -EINVAL;
232 }
233 
ipu7_isys_subdev_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * state)234 static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd,
235 				       struct v4l2_subdev_state *state)
236 {
237 	struct v4l2_subdev_route route = {
238 		.sink_pad = 0,
239 		.sink_stream = 0,
240 		.source_pad = 1,
241 		.source_stream = 0,
242 		.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
243 	};
244 	struct v4l2_subdev_krouting routing = {
245 		.num_routes = 1,
246 		.routes = &route,
247 	};
248 
249 	return subdev_set_routing(sd, state, &routing);
250 }
251 
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)252 int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
253 				 struct v4l2_subdev_state *state,
254 				 enum v4l2_subdev_format_whence which,
255 				 struct v4l2_subdev_krouting *routing)
256 {
257 	return subdev_set_routing(sd, state, routing);
258 }
259 
260 static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = {
261 	.init_state = ipu7_isys_subdev_init_state,
262 };
263 
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)264 int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
265 			  const struct v4l2_subdev_ops *ops,
266 			  unsigned int nr_ctrls,
267 			  unsigned int num_sink_pads,
268 			  unsigned int num_source_pads)
269 {
270 	unsigned int num_pads = num_sink_pads + num_source_pads;
271 	unsigned int i;
272 	int ret;
273 
274 	v4l2_subdev_init(&asd->sd, ops);
275 
276 	asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
277 			 V4L2_SUBDEV_FL_HAS_EVENTS |
278 			 V4L2_SUBDEV_FL_STREAMS;
279 	asd->sd.owner = THIS_MODULE;
280 	asd->sd.dev = &asd->isys->adev->auxdev.dev;
281 	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
282 	asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops;
283 
284 	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
285 				sizeof(*asd->pad), GFP_KERNEL);
286 	if (!asd->pad)
287 		return -ENOMEM;
288 
289 	for (i = 0; i < num_sink_pads; i++)
290 		asd->pad[i].flags = MEDIA_PAD_FL_SINK |
291 			MEDIA_PAD_FL_MUST_CONNECT;
292 
293 	for (i = num_sink_pads; i < num_pads; i++)
294 		asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
295 
296 	ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
297 	if (ret) {
298 		pr_err("isys subdev init failed %d.\n", ret);
299 		return ret;
300 	}
301 
302 	if (asd->ctrl_init) {
303 		ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
304 		if (ret)
305 			goto out_media_entity_cleanup;
306 
307 		asd->ctrl_init(&asd->sd);
308 		if (asd->ctrl_handler.error) {
309 			ret = asd->ctrl_handler.error;
310 			goto out_v4l2_ctrl_handler_free;
311 		}
312 
313 		asd->sd.ctrl_handler = &asd->ctrl_handler;
314 	}
315 
316 	asd->source = -1;
317 
318 	return 0;
319 
320 out_v4l2_ctrl_handler_free:
321 	v4l2_ctrl_handler_free(&asd->ctrl_handler);
322 
323 out_media_entity_cleanup:
324 	media_entity_cleanup(&asd->sd.entity);
325 
326 	return ret;
327 }
328 
ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev * asd)329 void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd)
330 {
331 	media_entity_cleanup(&asd->sd.entity);
332 	v4l2_ctrl_handler_free(&asd->ctrl_handler);
333 }
334