File Index
Files and directories
Sign in
FILE

ipu7-isys-subdev.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/ipu7/ipu7-isys-subdev.c C
  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
 22unsigned 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
 56bool 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
 73u32 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
101int 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
169int 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
187static 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
216int 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
234static 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
252int 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
260static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = {
261	.init_state = ipu7_isys_subdev_init_state,
262};
263
264int 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
320out_v4l2_ctrl_handler_free:
321	v4l2_ctrl_handler_free(&asd->ctrl_handler);
322
323out_media_entity_cleanup:
324	media_entity_cleanup(&asd->sd.entity);
325
326	return ret;
327}
328
329void 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}
~/ Read-only file preview.