File Index
Files and directories
Sign in
FILE

pisp-fe.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * PiSP Front End Driver
  4 *
  5 * Copyright (c) 2021-2024 Raspberry Pi Ltd.
  6 */
  7
  8#include <linux/bitops.h>
  9#include <linux/delay.h>
 10#include <linux/moduleparam.h>
 11#include <linux/pm_runtime.h>
 12#include <linux/seq_file.h>
 13
 14#include <media/videobuf2-dma-contig.h>
 15
 16#include "cfe.h"
 17#include "pisp-fe.h"
 18
 19#include "cfe-trace.h"
 20
 21#define FE_VERSION		0x000
 22#define FE_CONTROL		0x004
 23#define FE_STATUS		0x008
 24#define FE_FRAME_STATUS		0x00c
 25#define FE_ERROR_STATUS		0x010
 26#define FE_OUTPUT_STATUS	0x014
 27#define FE_INT_EN		0x018
 28#define FE_INT_STATUS		0x01c
 29
 30/* CONTROL */
 31#define FE_CONTROL_QUEUE	BIT(0)
 32#define FE_CONTROL_ABORT	BIT(1)
 33#define FE_CONTROL_RESET	BIT(2)
 34#define FE_CONTROL_LATCH_REGS	BIT(3)
 35
 36/* INT_EN / INT_STATUS */
 37#define FE_INT_EOF		BIT(0)
 38#define FE_INT_SOF		BIT(1)
 39#define FE_INT_LINES0		BIT(8)
 40#define FE_INT_LINES1		BIT(9)
 41#define FE_INT_STATS		BIT(16)
 42#define FE_INT_QREADY		BIT(24)
 43
 44/* STATUS */
 45#define FE_STATUS_QUEUED	BIT(0)
 46#define FE_STATUS_WAITING	BIT(1)
 47#define FE_STATUS_ACTIVE	BIT(2)
 48
 49#define PISP_FE_CONFIG_BASE_OFFSET	0x0040
 50
 51#define PISP_FE_ENABLE_STATS_CLUSTER \
 52	(PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE    | \
 53	 PISP_FE_ENABLE_BLC        | PISP_FE_ENABLE_CDAF_STATS  | \
 54	 PISP_FE_ENABLE_AWB_STATS  | PISP_FE_ENABLE_RGBY        | \
 55	 PISP_FE_ENABLE_LSC        | PISP_FE_ENABLE_AGC_STATS)
 56
 57#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i)				\
 58	((PISP_FE_ENABLE_CROP0     | PISP_FE_ENABLE_DOWNSCALE0 |	\
 59	  PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i)))
 60
 61struct pisp_fe_config_param {
 62	u32 dirty_flags;
 63	u32 dirty_flags_extra;
 64	size_t offset;
 65	size_t size;
 66};
 67
 68static const struct pisp_fe_config_param pisp_fe_config_map[] = {
 69	/* *_dirty_flag_extra types */
 70	{ 0, PISP_FE_DIRTY_GLOBAL,
 71		offsetof(struct pisp_fe_config, global),
 72		sizeof(struct pisp_fe_global_config) },
 73	{ 0, PISP_FE_DIRTY_FLOATING,
 74		offsetof(struct pisp_fe_config, floating_stats),
 75		sizeof(struct pisp_fe_floating_stats_config) },
 76	{ 0, PISP_FE_DIRTY_OUTPUT_AXI,
 77		offsetof(struct pisp_fe_config, output_axi),
 78		sizeof(struct pisp_fe_output_axi_config) },
 79	/* *_dirty_flag types */
 80	{ PISP_FE_ENABLE_INPUT, 0,
 81		offsetof(struct pisp_fe_config, input),
 82		sizeof(struct pisp_fe_input_config) },
 83	{ PISP_FE_ENABLE_DECOMPRESS, 0,
 84		offsetof(struct pisp_fe_config, decompress),
 85		sizeof(struct pisp_decompress_config) },
 86	{ PISP_FE_ENABLE_DECOMPAND, 0,
 87		offsetof(struct pisp_fe_config, decompand),
 88		sizeof(struct pisp_fe_decompand_config) },
 89	{ PISP_FE_ENABLE_BLA, 0,
 90		offsetof(struct pisp_fe_config, bla),
 91		sizeof(struct pisp_bla_config) },
 92	{ PISP_FE_ENABLE_DPC, 0,
 93		offsetof(struct pisp_fe_config, dpc),
 94		sizeof(struct pisp_fe_dpc_config) },
 95	{ PISP_FE_ENABLE_STATS_CROP, 0,
 96		offsetof(struct pisp_fe_config, stats_crop),
 97		sizeof(struct pisp_fe_crop_config) },
 98	{ PISP_FE_ENABLE_BLC, 0,
 99		offsetof(struct pisp_fe_config, blc),
100		sizeof(struct pisp_bla_config) },
101	{ PISP_FE_ENABLE_CDAF_STATS, 0,
102		offsetof(struct pisp_fe_config, cdaf_stats),
103		sizeof(struct pisp_fe_cdaf_stats_config) },
104	{ PISP_FE_ENABLE_AWB_STATS, 0,
105		offsetof(struct pisp_fe_config, awb_stats),
106		sizeof(struct pisp_fe_awb_stats_config) },
107	{ PISP_FE_ENABLE_RGBY, 0,
108		offsetof(struct pisp_fe_config, rgby),
109		sizeof(struct pisp_fe_rgby_config) },
110	{ PISP_FE_ENABLE_LSC, 0,
111		offsetof(struct pisp_fe_config, lsc),
112		sizeof(struct pisp_fe_lsc_config) },
113	{ PISP_FE_ENABLE_AGC_STATS, 0,
114		offsetof(struct pisp_fe_config, agc_stats),
115		sizeof(struct pisp_agc_statistics) },
116	{ PISP_FE_ENABLE_CROP0, 0,
117		offsetof(struct pisp_fe_config, ch[0].crop),
118		sizeof(struct pisp_fe_crop_config) },
119	{ PISP_FE_ENABLE_DOWNSCALE0, 0,
120		offsetof(struct pisp_fe_config, ch[0].downscale),
121		sizeof(struct pisp_fe_downscale_config) },
122	{ PISP_FE_ENABLE_COMPRESS0, 0,
123		offsetof(struct pisp_fe_config, ch[0].compress),
124		sizeof(struct pisp_compress_config) },
125	{ PISP_FE_ENABLE_OUTPUT0, 0,
126		offsetof(struct pisp_fe_config, ch[0].output),
127		sizeof(struct pisp_fe_output_config) },
128	{ PISP_FE_ENABLE_CROP1, 0,
129		offsetof(struct pisp_fe_config, ch[1].crop),
130		sizeof(struct pisp_fe_crop_config) },
131	{ PISP_FE_ENABLE_DOWNSCALE1, 0,
132		offsetof(struct pisp_fe_config, ch[1].downscale),
133		sizeof(struct pisp_fe_downscale_config) },
134	{ PISP_FE_ENABLE_COMPRESS1, 0,
135		offsetof(struct pisp_fe_config, ch[1].compress),
136		sizeof(struct pisp_compress_config) },
137	{ PISP_FE_ENABLE_OUTPUT1, 0,
138		offsetof(struct pisp_fe_config, ch[1].output),
139		sizeof(struct pisp_fe_output_config) },
140};
141
142#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg)
143#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg)
144#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg)
145
146static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset)
147{
148	return readl(fe->base + offset);
149}
150
151static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset,
152				     u32 val)
153{
154	writel(val, fe->base + offset);
155}
156
157static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe,
158					     u32 offset, u32 val)
159{
160	writel_relaxed(val, fe->base + offset);
161}
162
163static int pisp_fe_regs_show(struct seq_file *s, void *data)
164{
165	struct pisp_fe_device *fe = s->private;
166	int ret;
167
168	ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev);
169	if (ret)
170		return ret;
171
172	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
173
174#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg))
175	DUMP(FE_VERSION);
176	DUMP(FE_CONTROL);
177	DUMP(FE_STATUS);
178	DUMP(FE_FRAME_STATUS);
179	DUMP(FE_ERROR_STATUS);
180	DUMP(FE_OUTPUT_STATUS);
181	DUMP(FE_INT_EN);
182	DUMP(FE_INT_STATUS);
183#undef DUMP
184
185	pm_runtime_put(fe->v4l2_dev->dev);
186
187	return 0;
188}
189
190DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs);
191
192static void pisp_fe_config_write(struct pisp_fe_device *fe,
193				 struct pisp_fe_config *config,
194				 unsigned int start_offset, unsigned int size)
195{
196	const unsigned int max_offset =
197		offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]);
198	unsigned int end_offset;
199	u32 *cfg = (u32 *)config;
200
201	start_offset = min(start_offset, max_offset);
202	end_offset = min(start_offset + size, max_offset);
203
204	cfg += start_offset >> 2;
205	for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++)
206		pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i,
207					  *cfg);
208}
209
210void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof)
211{
212	u32 status, int_status, out_status, frame_status, error_status;
213
214	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
215	status = pisp_fe_reg_read(fe, FE_STATUS);
216	out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS);
217	frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS);
218	error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS);
219
220	int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
221	pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
222
223	trace_fe_irq(status, out_status, frame_status, error_status,
224		     int_status);
225
226	/* We do not report interrupts for the input/stream pad. */
227	for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) {
228		sof[i] = !!(int_status & FE_INT_SOF);
229		eof[i] = !!(int_status & FE_INT_EOF);
230	}
231}
232
233static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg,
234				    unsigned int c, struct v4l2_format const *f)
235{
236	unsigned int wbytes;
237
238	wbytes = cfg->ch[c].output.format.width;
239	if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK)
240		wbytes *= 2;
241
242	/* Check output image dimensions are nonzero and not too big */
243	if (cfg->ch[c].output.format.width < 2 ||
244	    cfg->ch[c].output.format.height < 2 ||
245	    cfg->ch[c].output.format.height > f->fmt.pix.height ||
246	    cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline ||
247	    wbytes > f->fmt.pix.bytesperline)
248		return false;
249
250	/* Check for zero-sized crops, which could cause lockup */
251	if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) &&
252	    ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||
253	      cfg->ch[c].crop.offset_y >= cfg->input.format.height ||
254	      cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2)))
255		return false;
256
257	if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) &&
258	    (cfg->ch[c].downscale.output_width < 2 ||
259	     cfg->ch[c].downscale.output_height < 2))
260		return false;
261
262	return true;
263}
264
265static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg)
266{
267	/* Check for zero-sized crop, which could cause lockup */
268	return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) ||
269		(cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) &&
270		 cfg->stats_crop.offset_y < cfg->input.format.height &&
271		 cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2));
272}
273
274int pisp_fe_validate_config(struct pisp_fe_device *fe,
275			    struct pisp_fe_config *cfg,
276			    struct v4l2_format const *f0,
277			    struct v4l2_format const *f1)
278{
279	/*
280	 * Check the input is enabled, streaming and has nonzero size;
281	 * to avoid cases where the hardware might lock up or try to
282	 * read inputs from memory (which this driver doesn't support).
283	 */
284	if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) ||
285	    cfg->input.streaming != 1 || cfg->input.format.width < 2 ||
286	    cfg->input.format.height < 2) {
287		pisp_fe_err(fe, "%s: Input config not valid", __func__);
288		return -EINVAL;
289	}
290
291	for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
292		if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) {
293			if (cfg->global.enables &
294					PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) {
295				pisp_fe_err(fe, "%s: Output %u not valid",
296					    __func__, i);
297				return -EINVAL;
298			}
299			continue;
300		}
301
302		if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0))
303			return -EINVAL;
304	}
305
306	if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) &&
307	    !pisp_fe_validate_stats(cfg)) {
308		pisp_fe_err(fe, "%s: Stats config not valid", __func__);
309		return -EINVAL;
310	}
311
312	return 0;
313}
314
315void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
316			struct pisp_fe_config *cfg)
317{
318	u64 addr;
319	u32 status;
320
321	/*
322	 * Check output buffers exist and outputs are correctly configured.
323	 * If valid, set the buffer's DMA address; otherwise disable.
324	 */
325	for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
326		struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i];
327
328		if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i)))
329			continue;
330
331		addr = vb2_dma_contig_plane_dma_addr(buf, 0);
332		cfg->output_buffer[i].addr_lo = addr & 0xffffffff;
333		cfg->output_buffer[i].addr_hi = addr >> 32;
334	}
335
336	if (vb2_bufs[FE_STATS_PAD]) {
337		addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0);
338		cfg->stats_buffer.addr_lo = addr & 0xffffffff;
339		cfg->stats_buffer.addr_hi = addr >> 32;
340	}
341
342	/* Set up ILINES interrupts 3/4 of the way down each output */
343	cfg->ch[0].output.ilines =
344		max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2);
345	cfg->ch[1].output.ilines =
346		max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2);
347
348	/*
349	 * The hardware must have consumed the previous config by now.
350	 * This read of status also serves as a memory barrier before the
351	 * sequence of relaxed writes which follow.
352	 */
353	status = pisp_fe_reg_read(fe, FE_STATUS);
354	if (WARN_ON(status & FE_STATUS_QUEUED))
355		return;
356
357	/*
358	 * Unconditionally write buffers, global and input parameters.
359	 * Write cropping and output parameters whenever they are enabled.
360	 * Selectively write other parameters that have been marked as
361	 * changed through the dirty flags.
362	 */
363	pisp_fe_config_write(fe, cfg, 0,
364			     offsetof(struct pisp_fe_config, decompress));
365	cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL;
366	cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT;
367	cfg->dirty_flags |= (cfg->global.enables &
368			     (PISP_FE_ENABLE_STATS_CROP        |
369			      PISP_FE_ENABLE_OUTPUT_CLUSTER(0) |
370			      PISP_FE_ENABLE_OUTPUT_CLUSTER(1)));
371	for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) {
372		const struct pisp_fe_config_param *p = &pisp_fe_config_map[i];
373
374		if (cfg->dirty_flags & p->dirty_flags ||
375		    cfg->dirty_flags_extra & p->dirty_flags_extra)
376			pisp_fe_config_write(fe, cfg, p->offset, p->size);
377	}
378
379	/* This final non-relaxed write serves as a memory barrier */
380	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE);
381}
382
383void pisp_fe_start(struct pisp_fe_device *fe)
384{
385	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
386	pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
387	pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF |
388					 FE_INT_LINES0 | FE_INT_LINES1);
389	fe->inframe_count = 0;
390}
391
392void pisp_fe_stop(struct pisp_fe_device *fe)
393{
394	pisp_fe_reg_write(fe, FE_INT_EN, 0);
395	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
396	usleep_range(1000, 2000);
397	WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
398	pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
399}
400
401static int pisp_fe_init_state(struct v4l2_subdev *sd,
402			      struct v4l2_subdev_state *state)
403{
404	struct v4l2_mbus_framefmt *fmt;
405
406	fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
407	*fmt = cfe_default_format;
408	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
409
410	fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD);
411	fmt->code = MEDIA_BUS_FMT_FIXED;
412	fmt->width = sizeof(struct pisp_fe_config);
413	fmt->height = 1;
414
415	fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
416	*fmt = cfe_default_format;
417	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
418
419	fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
420	*fmt = cfe_default_format;
421	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
422
423	fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD);
424	fmt->code = MEDIA_BUS_FMT_FIXED;
425	fmt->width = sizeof(struct pisp_statistics);
426	fmt->height = 1;
427
428	return 0;
429}
430
431static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
432			       struct v4l2_subdev_state *state,
433			       struct v4l2_subdev_format *format)
434{
435	struct v4l2_mbus_framefmt *fmt;
436	const struct cfe_fmt *cfe_fmt;
437
438	/* TODO: format propagation to source pads */
439	/* TODO: format validation */
440
441	switch (format->pad) {
442	case FE_STREAM_PAD:
443		cfe_fmt = find_format_by_code(format->format.code);
444		if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
445			cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
446
447		format->format.code = cfe_fmt->code;
448		format->format.field = V4L2_FIELD_NONE;
449
450		fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
451		*fmt = format->format;
452
453		fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
454		*fmt = format->format;
455
456		fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
457		*fmt = format->format;
458
459		return 0;
460
461	case FE_OUTPUT0_PAD:
462	case FE_OUTPUT1_PAD: {
463		/*
464		 * TODO: we should allow scaling and cropping by allowing the
465		 * user to set the size here.
466		 */
467		struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
468		u32 sink_code;
469		u32 code;
470
471		cfe_fmt = find_format_by_code(format->format.code);
472		if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
473			cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
474
475		format->format.code = cfe_fmt->code;
476
477		sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
478		if (!sink_fmt)
479			return -EINVAL;
480
481		source_fmt = v4l2_subdev_state_get_format(state, format->pad);
482		if (!source_fmt)
483			return -EINVAL;
484
485		sink_code = sink_fmt->code;
486		code = format->format.code;
487
488		/*
489		 * If the source code from the user does not match the code in
490		 * the sink pad, check that the source code matches the
491		 * compressed version of the sink code.
492		 */
493
494		if (code != sink_code &&
495		    code == cfe_find_compressed_code(sink_code))
496			source_fmt->code = code;
497
498		return 0;
499	}
500
501	case FE_CONFIG_PAD:
502	case FE_STATS_PAD:
503	default:
504		return v4l2_subdev_get_fmt(sd, state, format);
505	}
506}
507
508static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
509	.get_fmt = v4l2_subdev_get_fmt,
510	.set_fmt = pisp_fe_pad_set_fmt,
511	.link_validate = v4l2_subdev_link_validate_default,
512};
513
514static int pisp_fe_link_validate(struct media_link *link)
515{
516	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity);
517	struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd);
518
519	pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
520		    link->source->entity->name, link->source->index,
521		    link->sink->entity->name, link->sink->index);
522
523	if (link->sink->index == FE_STREAM_PAD)
524		return v4l2_subdev_link_validate(link);
525
526	if (link->sink->index == FE_CONFIG_PAD)
527		return 0;
528
529	return -EINVAL;
530}
531
532static const struct media_entity_operations pisp_fe_entity_ops = {
533	.link_validate = pisp_fe_link_validate,
534};
535
536static const struct v4l2_subdev_ops pisp_fe_subdev_ops = {
537	.pad = &pisp_fe_subdev_pad_ops,
538};
539
540static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = {
541	.init_state = pisp_fe_init_state,
542};
543
544int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
545{
546	int ret;
547
548	debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops);
549
550	fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);
551	pisp_fe_info(fe, "PiSP FE HW v%u.%u\n",
552		     (fe->hw_revision >> 24) & 0xff,
553		     (fe->hw_revision >> 20) & 0x0f);
554
555	fe->pad[FE_STREAM_PAD].flags =
556		MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
557	fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;
558	fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;
559	fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;
560	fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;
561
562	ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),
563				     fe->pad);
564	if (ret)
565		return ret;
566
567	/* Initialize subdev */
568	v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);
569	fe->sd.internal_ops = &pisp_fe_internal_ops;
570	fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
571	fe->sd.entity.ops = &pisp_fe_entity_ops;
572	fe->sd.entity.name = "pisp-fe";
573	fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
574	fe->sd.owner = THIS_MODULE;
575	snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");
576
577	ret = v4l2_subdev_init_finalize(&fe->sd);
578	if (ret)
579		goto err_entity_cleanup;
580
581	ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);
582	if (ret) {
583		pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret);
584		goto err_subdev_cleanup;
585	}
586
587	/* Must be in IDLE state (STATUS == 0) here. */
588	WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
589
590	return 0;
591
592err_subdev_cleanup:
593	v4l2_subdev_cleanup(&fe->sd);
594err_entity_cleanup:
595	media_entity_cleanup(&fe->sd.entity);
596
597	return ret;
598}
599
600void pisp_fe_uninit(struct pisp_fe_device *fe)
601{
602	v4l2_device_unregister_subdev(&fe->sd);
603	v4l2_subdev_cleanup(&fe->sd);
604	media_entity_cleanup(&fe->sd.entity);
605}
~/ Read-only file preview.