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