File Index
Files and directories
Sign in
FILE

stf-isp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/starfive/camss/stf-isp.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * stf_isp.c
  4 *
  5 * StarFive Camera Subsystem - ISP Module
  6 *
  7 * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
  8 */
  9#include <media/v4l2-rect.h>
 10
 11#include "stf-camss.h"
 12
 13static int isp_set_selection(struct v4l2_subdev *sd,
 14			     struct v4l2_subdev_state *state,
 15			     struct v4l2_subdev_selection *sel);
 16
 17static const struct stf_isp_format isp_formats_sink[] = {
 18	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
 19	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
 20	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
 21	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
 22};
 23
 24static const struct stf_isp_format isp_formats_source[] = {
 25	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8 },
 26};
 27
 28static const struct stf_isp_format_table isp_formats_st7110[] = {
 29	{ isp_formats_sink, ARRAY_SIZE(isp_formats_sink) },
 30	{ isp_formats_source, ARRAY_SIZE(isp_formats_source) },
 31};
 32
 33static const struct stf_isp_format *
 34stf_g_fmt_by_mcode(const struct stf_isp_format_table *fmt_table, u32 mcode)
 35{
 36	unsigned int i;
 37
 38	for (i = 0; i < fmt_table->nfmts; i++) {
 39		if (fmt_table->fmts[i].code == mcode)
 40			return &fmt_table->fmts[i];
 41	}
 42
 43	return NULL;
 44}
 45
 46int stf_isp_init(struct stfcamss *stfcamss)
 47{
 48	struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
 49
 50	isp_dev->stfcamss = stfcamss;
 51	isp_dev->formats = isp_formats_st7110;
 52	isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110);
 53	isp_dev->current_fmt = &isp_formats_source[0];
 54
 55	return 0;
 56}
 57
 58static int isp_set_stream(struct v4l2_subdev *sd, int enable)
 59{
 60	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
 61	struct v4l2_subdev_state *sd_state;
 62	struct v4l2_mbus_framefmt *fmt;
 63	struct v4l2_rect *crop;
 64
 65	sd_state = v4l2_subdev_lock_and_get_active_state(sd);
 66	fmt = v4l2_subdev_state_get_format(sd_state, STF_ISP_PAD_SINK);
 67	crop = v4l2_subdev_state_get_crop(sd_state, STF_ISP_PAD_SRC);
 68
 69	if (enable) {
 70		stf_isp_reset(isp_dev);
 71		stf_isp_init_cfg(isp_dev);
 72		stf_isp_settings(isp_dev, crop, fmt->code);
 73		stf_isp_stream_set(isp_dev);
 74	}
 75
 76	v4l2_subdev_call(isp_dev->source_subdev, video, s_stream, enable);
 77
 78	v4l2_subdev_unlock_state(sd_state);
 79	return 0;
 80}
 81
 82static void isp_try_format(struct stf_isp_dev *isp_dev,
 83			   struct v4l2_subdev_state *state,
 84			   unsigned int pad,
 85			   struct v4l2_mbus_framefmt *fmt)
 86{
 87	const struct stf_isp_format_table *formats;
 88
 89	if (pad >= STF_ISP_PAD_MAX) {
 90		fmt->colorspace = V4L2_COLORSPACE_SRGB;
 91		return;
 92	}
 93
 94	formats = &isp_dev->formats[pad];
 95
 96	fmt->width = clamp_t(u32, fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
 97			     STFCAMSS_FRAME_MAX_WIDTH);
 98	fmt->height = clamp_t(u32, fmt->height, STFCAMSS_FRAME_MIN_HEIGHT,
 99			      STFCAMSS_FRAME_MAX_HEIGHT);
100	fmt->height &= ~0x1;
101	fmt->field = V4L2_FIELD_NONE;
102	fmt->colorspace = V4L2_COLORSPACE_SRGB;
103	fmt->flags = 0;
104
105	if (!stf_g_fmt_by_mcode(formats, fmt->code))
106		fmt->code = formats->fmts[0].code;
107}
108
109static int isp_enum_mbus_code(struct v4l2_subdev *sd,
110			      struct v4l2_subdev_state *state,
111			      struct v4l2_subdev_mbus_code_enum *code)
112{
113	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
114	const struct stf_isp_format_table *formats;
115
116	if (code->pad == STF_ISP_PAD_SINK) {
117		if (code->index >= ARRAY_SIZE(isp_formats_sink))
118			return -EINVAL;
119
120		formats = &isp_dev->formats[code->pad];
121		code->code = formats->fmts[code->index].code;
122	} else {
123		struct v4l2_mbus_framefmt *sink_fmt;
124
125		if (code->index >= ARRAY_SIZE(isp_formats_source))
126			return -EINVAL;
127
128		sink_fmt = v4l2_subdev_state_get_format(state,
129							STF_ISP_PAD_SRC);
130
131		code->code = sink_fmt->code;
132		if (!code->code)
133			return -EINVAL;
134	}
135	code->flags = 0;
136
137	return 0;
138}
139
140static int isp_set_format(struct v4l2_subdev *sd,
141			  struct v4l2_subdev_state *state,
142			  struct v4l2_subdev_format *fmt)
143{
144	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
145	struct v4l2_mbus_framefmt *format;
146
147	format = v4l2_subdev_state_get_format(state, fmt->pad);
148	if (!format)
149		return -EINVAL;
150
151	isp_try_format(isp_dev, state, fmt->pad, &fmt->format);
152	*format = fmt->format;
153
154	isp_dev->current_fmt = stf_g_fmt_by_mcode(&isp_dev->formats[fmt->pad],
155						  fmt->format.code);
156
157	/* Propagate to in crop */
158	if (fmt->pad == STF_ISP_PAD_SINK) {
159		struct v4l2_subdev_selection sel = { 0 };
160
161		/* Reset sink pad compose selection */
162		sel.which = fmt->which;
163		sel.pad = STF_ISP_PAD_SINK;
164		sel.target = V4L2_SEL_TGT_CROP;
165		sel.r.width = fmt->format.width;
166		sel.r.height = fmt->format.height;
167		isp_set_selection(sd, state, &sel);
168	}
169
170	return 0;
171}
172
173static const struct v4l2_rect stf_frame_min_crop = {
174	.width = STFCAMSS_FRAME_MIN_WIDTH,
175	.height = STFCAMSS_FRAME_MIN_HEIGHT,
176	.top = 0,
177	.left = 0,
178};
179
180static void isp_try_crop(struct stf_isp_dev *isp_dev,
181			 struct v4l2_subdev_state *state,
182			 struct v4l2_rect *crop)
183{
184	struct v4l2_mbus_framefmt *fmt =
185		v4l2_subdev_state_get_format(state, STF_ISP_PAD_SINK);
186
187	const struct v4l2_rect bounds = {
188		.width = fmt->width,
189		.height = fmt->height,
190		.left = 0,
191		.top = 0,
192	};
193
194	v4l2_rect_set_min_size(crop, &stf_frame_min_crop);
195	v4l2_rect_map_inside(crop, &bounds);
196}
197
198static int isp_get_selection(struct v4l2_subdev *sd,
199			     struct v4l2_subdev_state *state,
200			     struct v4l2_subdev_selection *sel)
201{
202	struct v4l2_subdev_format fmt = { 0 };
203	struct v4l2_rect *rect;
204
205	switch (sel->target) {
206	case V4L2_SEL_TGT_CROP_BOUNDS:
207		if (sel->pad == STF_ISP_PAD_SINK) {
208			fmt.format = *v4l2_subdev_state_get_format(state,
209								   sel->pad);
210			sel->r.left = 0;
211			sel->r.top = 0;
212			sel->r.width = fmt.format.width;
213			sel->r.height = fmt.format.height;
214		} else if (sel->pad == STF_ISP_PAD_SRC) {
215			rect = v4l2_subdev_state_get_crop(state, sel->pad);
216			sel->r = *rect;
217		}
218		break;
219
220	case V4L2_SEL_TGT_CROP:
221		rect = v4l2_subdev_state_get_crop(state, sel->pad);
222		if (!rect)
223			return -EINVAL;
224
225		sel->r = *rect;
226		break;
227
228	default:
229		return -EINVAL;
230	}
231
232	return 0;
233}
234
235static int isp_set_selection(struct v4l2_subdev *sd,
236			     struct v4l2_subdev_state *state,
237			     struct v4l2_subdev_selection *sel)
238{
239	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
240	struct v4l2_rect *rect;
241
242	if (sel->target != V4L2_SEL_TGT_CROP)
243		return -EINVAL;
244
245	if (sel->target == V4L2_SEL_TGT_CROP &&
246	    sel->pad == STF_ISP_PAD_SINK) {
247		struct v4l2_subdev_selection crop = { 0 };
248
249		rect = v4l2_subdev_state_get_crop(state, sel->pad);
250		if (!rect)
251			return -EINVAL;
252
253		isp_try_crop(isp_dev, state, &sel->r);
254		*rect = sel->r;
255
256		/* Reset source crop selection */
257		crop.which = sel->which;
258		crop.pad = STF_ISP_PAD_SRC;
259		crop.target = V4L2_SEL_TGT_CROP;
260		crop.r = *rect;
261		isp_set_selection(sd, state, &crop);
262	} else if (sel->target == V4L2_SEL_TGT_CROP &&
263		   sel->pad == STF_ISP_PAD_SRC) {
264		struct v4l2_subdev_format fmt = { 0 };
265
266		rect = v4l2_subdev_state_get_crop(state, sel->pad);
267		if (!rect)
268			return -EINVAL;
269
270		isp_try_crop(isp_dev, state, &sel->r);
271		*rect = sel->r;
272
273		/* Reset source pad format width and height */
274		fmt.which = sel->which;
275		fmt.pad = STF_ISP_PAD_SRC;
276		fmt.format.width = rect->width;
277		fmt.format.height = rect->height;
278		isp_set_format(sd, state, &fmt);
279	}
280
281	dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%ux%u\n",
282		sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
283
284	return 0;
285}
286
287static int isp_init_formats(struct v4l2_subdev *sd,
288			    struct v4l2_subdev_state *sd_state)
289{
290	struct v4l2_subdev_format format = {
291		.pad = STF_ISP_PAD_SINK,
292		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
293		.format = {
294			.code = MEDIA_BUS_FMT_SRGGB10_1X10,
295			.width = 1920,
296			.height = 1080
297		}
298	};
299
300	return isp_set_format(sd, sd_state, &format);
301}
302
303static const struct v4l2_subdev_video_ops isp_video_ops = {
304	.s_stream = isp_set_stream,
305};
306
307static const struct v4l2_subdev_pad_ops isp_pad_ops = {
308	.enum_mbus_code = isp_enum_mbus_code,
309	.get_fmt = v4l2_subdev_get_fmt,
310	.set_fmt = isp_set_format,
311	.get_selection = isp_get_selection,
312	.set_selection = isp_set_selection,
313};
314
315static const struct v4l2_subdev_ops isp_v4l2_ops = {
316	.video = &isp_video_ops,
317	.pad = &isp_pad_ops,
318};
319
320static const struct v4l2_subdev_internal_ops isp_internal_ops = {
321	.init_state = isp_init_formats,
322};
323
324static const struct media_entity_operations isp_media_ops = {
325	.link_validate = v4l2_subdev_link_validate,
326};
327
328int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev)
329{
330	struct v4l2_subdev *sd = &isp_dev->subdev;
331	struct media_pad *pads = isp_dev->pads;
332	int ret;
333
334	v4l2_subdev_init(sd, &isp_v4l2_ops);
335	sd->internal_ops = &isp_internal_ops;
336	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
337	snprintf(sd->name, ARRAY_SIZE(sd->name), "stf_isp");
338	v4l2_set_subdevdata(sd, isp_dev);
339
340	pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
341	pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
342
343	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
344	sd->entity.ops = &isp_media_ops;
345	ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads);
346	if (ret) {
347		dev_err(isp_dev->stfcamss->dev,
348			"Failed to init media entity: %d\n", ret);
349		return ret;
350	}
351
352	ret = v4l2_subdev_init_finalize(sd);
353	if (ret)
354		goto err_entity_cleanup;
355
356	ret = v4l2_device_register_subdev(v4l2_dev, sd);
357	if (ret) {
358		dev_err(isp_dev->stfcamss->dev,
359			"Failed to register subdev: %d\n", ret);
360		goto err_subdev_cleanup;
361	}
362
363	return 0;
364
365err_subdev_cleanup:
366	v4l2_subdev_cleanup(sd);
367err_entity_cleanup:
368	media_entity_cleanup(&sd->entity);
369	return ret;
370}
371
372int stf_isp_unregister(struct stf_isp_dev *isp_dev)
373{
374	v4l2_device_unregister_subdev(&isp_dev->subdev);
375	v4l2_subdev_cleanup(&isp_dev->subdev);
376	media_entity_cleanup(&isp_dev->subdev.entity);
377
378	return 0;
379}
~/ Read-only file preview.