File Index
Files and directories
Sign in
FILE

ipu6-isys-subdev.c

File preview with syntax highlighting.

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