xref: /linux/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1 *6edb685aSTomi Valkeinen // SPDX-License-Identifier: GPL-2.0-only
2 *6edb685aSTomi Valkeinen /*
3 *6edb685aSTomi Valkeinen  * RP1 CSI-2 Driver
4 *6edb685aSTomi Valkeinen  *
5 *6edb685aSTomi Valkeinen  * Copyright (c) 2021-2024 Raspberry Pi Ltd.
6 *6edb685aSTomi Valkeinen  * Copyright (c) 2023-2024 Ideas on Board Oy
7 *6edb685aSTomi Valkeinen  */
8 *6edb685aSTomi Valkeinen 
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 "csi2.h"
18 *6edb685aSTomi Valkeinen 
19 *6edb685aSTomi Valkeinen #include "cfe-trace.h"
20 *6edb685aSTomi Valkeinen 
21 *6edb685aSTomi Valkeinen static bool csi2_track_errors;
22 *6edb685aSTomi Valkeinen module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
23 *6edb685aSTomi Valkeinen MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
24 *6edb685aSTomi Valkeinen 
25 *6edb685aSTomi Valkeinen #define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg)
26 *6edb685aSTomi Valkeinen #define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg)
27 *6edb685aSTomi Valkeinen 
28 *6edb685aSTomi Valkeinen /* CSI2-DMA registers */
29 *6edb685aSTomi Valkeinen #define CSI2_STATUS		0x000
30 *6edb685aSTomi Valkeinen #define CSI2_QOS		0x004
31 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_OVERFLOW	0x008
32 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_INACTIVE	0x00c
33 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_UNMATCHED	0x010
34 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_LEN_LIMIT	0x014
35 *6edb685aSTomi Valkeinen 
36 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_AMOUNT_SHIFT	0
37 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_AMOUNT_MASK	GENMASK(23, 0)
38 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_DT_SHIFT		24
39 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_DT_MASK		GENMASK(29, 24)
40 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_VC_SHIFT		30
41 *6edb685aSTomi Valkeinen #define CSI2_DISCARDS_VC_MASK		GENMASK(31, 30)
42 *6edb685aSTomi Valkeinen 
43 *6edb685aSTomi Valkeinen #define CSI2_LLEV_PANICS	0x018
44 *6edb685aSTomi Valkeinen #define CSI2_ULEV_PANICS	0x01c
45 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK		0x020
46 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_OVERFLOW		BIT(0)
47 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW	BIT(1)
48 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT	BIT(2)
49 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED	BIT(3)
50 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE	BIT(4)
51 *6edb685aSTomi Valkeinen #define CSI2_IRQ_MASK_IRQ_ALL                                              \
52 *6edb685aSTomi Valkeinen 	(CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
53 *6edb685aSTomi Valkeinen 	 CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT |                          \
54 *6edb685aSTomi Valkeinen 	 CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED |                             \
55 *6edb685aSTomi Valkeinen 	 CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
56 *6edb685aSTomi Valkeinen 
57 *6edb685aSTomi Valkeinen #define CSI2_CTRL		0x024
58 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL(x)		((x) * 0x40 + 0x28)
59 *6edb685aSTomi Valkeinen #define CSI2_CH_ADDR0(x)	((x) * 0x40 + 0x2c)
60 *6edb685aSTomi Valkeinen #define CSI2_CH_ADDR1(x)	((x) * 0x40 + 0x3c)
61 *6edb685aSTomi Valkeinen #define CSI2_CH_STRIDE(x)	((x) * 0x40 + 0x30)
62 *6edb685aSTomi Valkeinen #define CSI2_CH_LENGTH(x)	((x) * 0x40 + 0x34)
63 *6edb685aSTomi Valkeinen #define CSI2_CH_DEBUG(x)	((x) * 0x40 + 0x38)
64 *6edb685aSTomi Valkeinen #define CSI2_CH_FRAME_SIZE(x)	((x) * 0x40 + 0x40)
65 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL(x)	((x) * 0x40 + 0x44)
66 *6edb685aSTomi Valkeinen #define CSI2_CH_FE_FRAME_ID(x)	((x) * 0x40 + 0x48)
67 *6edb685aSTomi Valkeinen 
68 *6edb685aSTomi Valkeinen /* CSI2_STATUS */
69 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FS(x)			(BIT(0) << (x))
70 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FE(x)			(BIT(4) << (x))
71 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_FE_ACK(x)		(BIT(8) << (x))
72 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_LE(x)			(BIT(12) << (x))
73 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_LE_ACK(x)		(BIT(16) << (x))
74 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_CH_MASK(x) \
75 *6edb685aSTomi Valkeinen 	(CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \
76 *6edb685aSTomi Valkeinen 	 CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \
77 *6edb685aSTomi Valkeinen 	 CSI2_STATUS_IRQ_LE_ACK(x))
78 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_OVERFLOW		BIT(20)
79 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_OVERFLOW	BIT(21)
80 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT	BIT(22)
81 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_UNMATCHED	BIT(23)
82 *6edb685aSTomi Valkeinen #define CSI2_STATUS_IRQ_DISCARD_INACTIVE	BIT(24)
83 *6edb685aSTomi Valkeinen 
84 *6edb685aSTomi Valkeinen /* CSI2_CTRL */
85 *6edb685aSTomi Valkeinen #define CSI2_CTRL_EOP_IS_EOL			BIT(0)
86 *6edb685aSTomi Valkeinen 
87 *6edb685aSTomi Valkeinen /* CSI2_CH_CTRL */
88 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_DMA_EN			BIT(0)
89 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_FORCE			BIT(3)
90 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_AUTO_ARM			BIT(4)
91 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FS			BIT(13)
92 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FE			BIT(14)
93 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_FE_ACK		BIT(15)
94 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_LE			BIT(16)
95 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_IRQ_EN_LE_ACK		BIT(17)
96 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_FLUSH_FE			BIT(28)
97 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_PACK_LINE			BIT(29)
98 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_PACK_BYTES			BIT(30)
99 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_CH_MODE_MASK		GENMASK(2, 1)
100 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_VC_MASK			GENMASK(6, 5)
101 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_DT_MASK			GENMASK(12, 7)
102 *6edb685aSTomi Valkeinen #define CSI2_CH_CTRL_LC_MASK			GENMASK(27, 18)
103 *6edb685aSTomi Valkeinen 
104 *6edb685aSTomi Valkeinen /* CHx_COMPRESSION_CONTROL */
105 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_OFFSET_MASK		GENMASK(15, 0)
106 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_SHIFT_MASK		GENMASK(19, 16)
107 *6edb685aSTomi Valkeinen #define CSI2_CH_COMP_CTRL_MODE_MASK		GENMASK(25, 24)
108 *6edb685aSTomi Valkeinen 
109 *6edb685aSTomi Valkeinen static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset)
110 *6edb685aSTomi Valkeinen {
111 *6edb685aSTomi Valkeinen 	return readl(csi2->base + offset);
112 *6edb685aSTomi Valkeinen }
113 *6edb685aSTomi Valkeinen 
114 *6edb685aSTomi Valkeinen static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
115 *6edb685aSTomi Valkeinen {
116 *6edb685aSTomi Valkeinen 	writel(val, csi2->base + offset);
117 *6edb685aSTomi Valkeinen }
118 *6edb685aSTomi Valkeinen 
119 *6edb685aSTomi Valkeinen static inline void set_field(u32 *valp, u32 field, u32 mask)
120 *6edb685aSTomi Valkeinen {
121 *6edb685aSTomi Valkeinen 	u32 val = *valp;
122 *6edb685aSTomi Valkeinen 
123 *6edb685aSTomi Valkeinen 	val &= ~mask;
124 *6edb685aSTomi Valkeinen 	val |= (field << __ffs(mask)) & mask;
125 *6edb685aSTomi Valkeinen 	*valp = val;
126 *6edb685aSTomi Valkeinen }
127 *6edb685aSTomi Valkeinen 
128 *6edb685aSTomi Valkeinen static int csi2_regs_show(struct seq_file *s, void *data)
129 *6edb685aSTomi Valkeinen {
130 *6edb685aSTomi Valkeinen 	struct csi2_device *csi2 = s->private;
131 *6edb685aSTomi Valkeinen 	int ret;
132 *6edb685aSTomi Valkeinen 
133 *6edb685aSTomi Valkeinen 	ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev);
134 *6edb685aSTomi Valkeinen 	if (ret)
135 *6edb685aSTomi Valkeinen 		return ret;
136 *6edb685aSTomi Valkeinen 
137 *6edb685aSTomi Valkeinen #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg))
138 *6edb685aSTomi Valkeinen #define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \
139 *6edb685aSTomi Valkeinen 				     csi2_reg_read(csi2, reg(idx)))
140 *6edb685aSTomi Valkeinen 
141 *6edb685aSTomi Valkeinen 	DUMP(CSI2_STATUS);
142 *6edb685aSTomi Valkeinen 	DUMP(CSI2_DISCARDS_OVERFLOW);
143 *6edb685aSTomi Valkeinen 	DUMP(CSI2_DISCARDS_INACTIVE);
144 *6edb685aSTomi Valkeinen 	DUMP(CSI2_DISCARDS_UNMATCHED);
145 *6edb685aSTomi Valkeinen 	DUMP(CSI2_DISCARDS_LEN_LIMIT);
146 *6edb685aSTomi Valkeinen 	DUMP(CSI2_LLEV_PANICS);
147 *6edb685aSTomi Valkeinen 	DUMP(CSI2_ULEV_PANICS);
148 *6edb685aSTomi Valkeinen 	DUMP(CSI2_IRQ_MASK);
149 *6edb685aSTomi Valkeinen 	DUMP(CSI2_CTRL);
150 *6edb685aSTomi Valkeinen 
151 *6edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) {
152 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_CTRL);
153 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_ADDR0);
154 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_ADDR1);
155 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_STRIDE);
156 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_LENGTH);
157 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_DEBUG);
158 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_FRAME_SIZE);
159 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_COMP_CTRL);
160 *6edb685aSTomi Valkeinen 		DUMP_CH(i, CSI2_CH_FE_FRAME_ID);
161 *6edb685aSTomi Valkeinen 	}
162 *6edb685aSTomi Valkeinen 
163 *6edb685aSTomi Valkeinen #undef DUMP
164 *6edb685aSTomi Valkeinen #undef DUMP_CH
165 *6edb685aSTomi Valkeinen 
166 *6edb685aSTomi Valkeinen 	pm_runtime_put(csi2->v4l2_dev->dev);
167 *6edb685aSTomi Valkeinen 
168 *6edb685aSTomi Valkeinen 	return 0;
169 *6edb685aSTomi Valkeinen }
170 *6edb685aSTomi Valkeinen 
171 *6edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(csi2_regs);
172 *6edb685aSTomi Valkeinen 
173 *6edb685aSTomi Valkeinen static int csi2_errors_show(struct seq_file *s, void *data)
174 *6edb685aSTomi Valkeinen {
175 *6edb685aSTomi Valkeinen 	struct csi2_device *csi2 = s->private;
176 *6edb685aSTomi Valkeinen 	unsigned long flags;
177 *6edb685aSTomi Valkeinen 	u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
178 *6edb685aSTomi Valkeinen 	u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
179 *6edb685aSTomi Valkeinen 	u32 overflows;
180 *6edb685aSTomi Valkeinen 
181 *6edb685aSTomi Valkeinen 	spin_lock_irqsave(&csi2->errors_lock, flags);
182 *6edb685aSTomi Valkeinen 
183 *6edb685aSTomi Valkeinen 	memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
184 *6edb685aSTomi Valkeinen 	memcpy(discards_dt_table, csi2->discards_dt_table,
185 *6edb685aSTomi Valkeinen 	       sizeof(discards_dt_table));
186 *6edb685aSTomi Valkeinen 	overflows = csi2->overflows;
187 *6edb685aSTomi Valkeinen 
188 *6edb685aSTomi Valkeinen 	csi2->overflows = 0;
189 *6edb685aSTomi Valkeinen 	memset(csi2->discards_table, 0, sizeof(discards_table));
190 *6edb685aSTomi Valkeinen 	memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
191 *6edb685aSTomi Valkeinen 
192 *6edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&csi2->errors_lock, flags);
193 *6edb685aSTomi Valkeinen 
194 *6edb685aSTomi Valkeinen 	seq_printf(s, "Overflows %u\n", overflows);
195 *6edb685aSTomi Valkeinen 	seq_puts(s, "Discards:\n");
196 *6edb685aSTomi Valkeinen 	seq_puts(s, "VC            OVLF        LEN  UNMATCHED   INACTIVE\n");
197 *6edb685aSTomi Valkeinen 
198 *6edb685aSTomi Valkeinen 	for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
199 *6edb685aSTomi Valkeinen 		seq_printf(s, "%u       %10u %10u %10u %10u\n", vc,
200 *6edb685aSTomi Valkeinen 			   discards_table[vc][DISCARDS_TABLE_OVERFLOW],
201 *6edb685aSTomi Valkeinen 			   discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
202 *6edb685aSTomi Valkeinen 			   discards_table[vc][DISCARDS_TABLE_UNMATCHED],
203 *6edb685aSTomi Valkeinen 			   discards_table[vc][DISCARDS_TABLE_INACTIVE]);
204 *6edb685aSTomi Valkeinen 	}
205 *6edb685aSTomi Valkeinen 
206 *6edb685aSTomi Valkeinen 	seq_printf(s, "Last DT %10u %10u %10u %10u\n",
207 *6edb685aSTomi Valkeinen 		   discards_dt_table[DISCARDS_TABLE_OVERFLOW],
208 *6edb685aSTomi Valkeinen 		   discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
209 *6edb685aSTomi Valkeinen 		   discards_dt_table[DISCARDS_TABLE_UNMATCHED],
210 *6edb685aSTomi Valkeinen 		   discards_dt_table[DISCARDS_TABLE_INACTIVE]);
211 *6edb685aSTomi Valkeinen 
212 *6edb685aSTomi Valkeinen 	return 0;
213 *6edb685aSTomi Valkeinen }
214 *6edb685aSTomi Valkeinen 
215 *6edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(csi2_errors);
216 *6edb685aSTomi Valkeinen 
217 *6edb685aSTomi Valkeinen static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
218 *6edb685aSTomi Valkeinen {
219 *6edb685aSTomi Valkeinen 	spin_lock(&csi2->errors_lock);
220 *6edb685aSTomi Valkeinen 
221 *6edb685aSTomi Valkeinen 	if (status & CSI2_STATUS_IRQ_OVERFLOW)
222 *6edb685aSTomi Valkeinen 		csi2->overflows++;
223 *6edb685aSTomi Valkeinen 
224 *6edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
225 *6edb685aSTomi Valkeinen 		static const u32 discard_bits[] = {
226 *6edb685aSTomi Valkeinen 			CSI2_STATUS_IRQ_DISCARD_OVERFLOW,
227 *6edb685aSTomi Valkeinen 			CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT,
228 *6edb685aSTomi Valkeinen 			CSI2_STATUS_IRQ_DISCARD_UNMATCHED,
229 *6edb685aSTomi Valkeinen 			CSI2_STATUS_IRQ_DISCARD_INACTIVE,
230 *6edb685aSTomi Valkeinen 		};
231 *6edb685aSTomi Valkeinen 		static const u8 discard_regs[] = {
232 *6edb685aSTomi Valkeinen 			CSI2_DISCARDS_OVERFLOW,
233 *6edb685aSTomi Valkeinen 			CSI2_DISCARDS_LEN_LIMIT,
234 *6edb685aSTomi Valkeinen 			CSI2_DISCARDS_UNMATCHED,
235 *6edb685aSTomi Valkeinen 			CSI2_DISCARDS_INACTIVE,
236 *6edb685aSTomi Valkeinen 		};
237 *6edb685aSTomi Valkeinen 		u32 amount;
238 *6edb685aSTomi Valkeinen 		u8 dt, vc;
239 *6edb685aSTomi Valkeinen 		u32 v;
240 *6edb685aSTomi Valkeinen 
241 *6edb685aSTomi Valkeinen 		if (!(status & discard_bits[i]))
242 *6edb685aSTomi Valkeinen 			continue;
243 *6edb685aSTomi Valkeinen 
244 *6edb685aSTomi Valkeinen 		v = csi2_reg_read(csi2, discard_regs[i]);
245 *6edb685aSTomi Valkeinen 		csi2_reg_write(csi2, discard_regs[i], 0);
246 *6edb685aSTomi Valkeinen 
247 *6edb685aSTomi Valkeinen 		amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
248 *6edb685aSTomi Valkeinen 			 CSI2_DISCARDS_AMOUNT_SHIFT;
249 *6edb685aSTomi Valkeinen 		dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
250 *6edb685aSTomi Valkeinen 		vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
251 *6edb685aSTomi Valkeinen 
252 *6edb685aSTomi Valkeinen 		csi2->discards_table[vc][i] += amount;
253 *6edb685aSTomi Valkeinen 		csi2->discards_dt_table[i] = dt;
254 *6edb685aSTomi Valkeinen 	}
255 *6edb685aSTomi Valkeinen 
256 *6edb685aSTomi Valkeinen 	spin_unlock(&csi2->errors_lock);
257 *6edb685aSTomi Valkeinen }
258 *6edb685aSTomi Valkeinen 
259 *6edb685aSTomi Valkeinen void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof)
260 *6edb685aSTomi Valkeinen {
261 *6edb685aSTomi Valkeinen 	u32 status;
262 *6edb685aSTomi Valkeinen 
263 *6edb685aSTomi Valkeinen 	status = csi2_reg_read(csi2, CSI2_STATUS);
264 *6edb685aSTomi Valkeinen 
265 *6edb685aSTomi Valkeinen 	/* Write value back to clear the interrupts */
266 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_STATUS, status);
267 *6edb685aSTomi Valkeinen 
268 *6edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
269 *6edb685aSTomi Valkeinen 		u32 dbg;
270 *6edb685aSTomi Valkeinen 
271 *6edb685aSTomi Valkeinen 		if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0)
272 *6edb685aSTomi Valkeinen 			continue;
273 *6edb685aSTomi Valkeinen 
274 *6edb685aSTomi Valkeinen 		dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
275 *6edb685aSTomi Valkeinen 
276 *6edb685aSTomi Valkeinen 		trace_csi2_irq(i, status, dbg);
277 *6edb685aSTomi Valkeinen 
278 *6edb685aSTomi Valkeinen 		sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i));
279 *6edb685aSTomi Valkeinen 		eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i));
280 *6edb685aSTomi Valkeinen 	}
281 *6edb685aSTomi Valkeinen 
282 *6edb685aSTomi Valkeinen 	if (csi2_track_errors)
283 *6edb685aSTomi Valkeinen 		csi2_isr_handle_errors(csi2, status);
284 *6edb685aSTomi Valkeinen }
285 *6edb685aSTomi Valkeinen 
286 *6edb685aSTomi Valkeinen void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
287 *6edb685aSTomi Valkeinen 		     dma_addr_t dmaaddr, unsigned int stride, unsigned int size)
288 *6edb685aSTomi Valkeinen {
289 *6edb685aSTomi Valkeinen 	u64 addr = dmaaddr;
290 *6edb685aSTomi Valkeinen 	/*
291 *6edb685aSTomi Valkeinen 	 * ADDRESS0 must be written last as it triggers the double buffering
292 *6edb685aSTomi Valkeinen 	 * mechanism for all buffer registers within the hardware.
293 *6edb685aSTomi Valkeinen 	 */
294 *6edb685aSTomi Valkeinen 	addr >>= 4;
295 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4);
296 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4);
297 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32);
298 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff);
299 *6edb685aSTomi Valkeinen }
300 *6edb685aSTomi Valkeinen 
301 *6edb685aSTomi Valkeinen void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
302 *6edb685aSTomi Valkeinen 			  enum csi2_compression_mode mode, unsigned int shift,
303 *6edb685aSTomi Valkeinen 			  unsigned int offset)
304 *6edb685aSTomi Valkeinen {
305 *6edb685aSTomi Valkeinen 	u32 compression = 0;
306 *6edb685aSTomi Valkeinen 
307 *6edb685aSTomi Valkeinen 	set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset);
308 *6edb685aSTomi Valkeinen 	set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift);
309 *6edb685aSTomi Valkeinen 	set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode);
310 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
311 *6edb685aSTomi Valkeinen }
312 *6edb685aSTomi Valkeinen 
313 *6edb685aSTomi Valkeinen void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
314 *6edb685aSTomi Valkeinen 			enum csi2_mode mode, bool auto_arm, bool pack_bytes,
315 *6edb685aSTomi Valkeinen 			unsigned int width, unsigned int height,
316 *6edb685aSTomi Valkeinen 			u8 vc, u8 dt)
317 *6edb685aSTomi Valkeinen {
318 *6edb685aSTomi Valkeinen 	u32 ctrl;
319 *6edb685aSTomi Valkeinen 
320 *6edb685aSTomi Valkeinen 	csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
321 *6edb685aSTomi Valkeinen 
322 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0);
323 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
324 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel));
325 *6edb685aSTomi Valkeinen 
326 *6edb685aSTomi Valkeinen 	/* Enable channel and FS/FE interrupts. */
327 *6edb685aSTomi Valkeinen 	ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS |
328 *6edb685aSTomi Valkeinen 	       CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE;
329 *6edb685aSTomi Valkeinen 	/* PACK_BYTES ensures no striding for embedded data. */
330 *6edb685aSTomi Valkeinen 	if (pack_bytes)
331 *6edb685aSTomi Valkeinen 		ctrl |= CSI2_CH_CTRL_PACK_BYTES;
332 *6edb685aSTomi Valkeinen 
333 *6edb685aSTomi Valkeinen 	if (auto_arm)
334 *6edb685aSTomi Valkeinen 		ctrl |= CSI2_CH_CTRL_AUTO_ARM;
335 *6edb685aSTomi Valkeinen 
336 *6edb685aSTomi Valkeinen 	if (width && height) {
337 *6edb685aSTomi Valkeinen 		set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK);
338 *6edb685aSTomi Valkeinen 		csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
339 *6edb685aSTomi Valkeinen 			       (height << 16) | width);
340 *6edb685aSTomi Valkeinen 	} else {
341 *6edb685aSTomi Valkeinen 		set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK);
342 *6edb685aSTomi Valkeinen 		csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
343 *6edb685aSTomi Valkeinen 	}
344 *6edb685aSTomi Valkeinen 
345 *6edb685aSTomi Valkeinen 	set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK);
346 *6edb685aSTomi Valkeinen 	set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK);
347 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
348 *6edb685aSTomi Valkeinen 	csi2->num_lines[channel] = height;
349 *6edb685aSTomi Valkeinen }
350 *6edb685aSTomi Valkeinen 
351 *6edb685aSTomi Valkeinen void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel)
352 *6edb685aSTomi Valkeinen {
353 *6edb685aSTomi Valkeinen 	csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
354 *6edb685aSTomi Valkeinen 
355 *6edb685aSTomi Valkeinen 	/* Channel disable.  Use FORCE to allow stopping mid-frame. */
356 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE);
357 *6edb685aSTomi Valkeinen 	/* Latch the above change by writing to the ADDR0 register. */
358 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
359 *6edb685aSTomi Valkeinen 	/* Write this again, the HW needs it! */
360 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
361 *6edb685aSTomi Valkeinen }
362 *6edb685aSTomi Valkeinen 
363 *6edb685aSTomi Valkeinen void csi2_open_rx(struct csi2_device *csi2)
364 *6edb685aSTomi Valkeinen {
365 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_IRQ_MASK,
366 *6edb685aSTomi Valkeinen 		       csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
367 *6edb685aSTomi Valkeinen 
368 *6edb685aSTomi Valkeinen 	dphy_start(&csi2->dphy);
369 *6edb685aSTomi Valkeinen 
370 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL);
371 *6edb685aSTomi Valkeinen }
372 *6edb685aSTomi Valkeinen 
373 *6edb685aSTomi Valkeinen void csi2_close_rx(struct csi2_device *csi2)
374 *6edb685aSTomi Valkeinen {
375 *6edb685aSTomi Valkeinen 	dphy_stop(&csi2->dphy);
376 *6edb685aSTomi Valkeinen 
377 *6edb685aSTomi Valkeinen 	csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
378 *6edb685aSTomi Valkeinen }
379 *6edb685aSTomi Valkeinen 
380 *6edb685aSTomi Valkeinen static int csi2_init_state(struct v4l2_subdev *sd,
381 *6edb685aSTomi Valkeinen 			   struct v4l2_subdev_state *state)
382 *6edb685aSTomi Valkeinen {
383 *6edb685aSTomi Valkeinen 	struct v4l2_subdev_route routes[] = { {
384 *6edb685aSTomi Valkeinen 		.sink_pad = CSI2_PAD_SINK,
385 *6edb685aSTomi Valkeinen 		.sink_stream = 0,
386 *6edb685aSTomi Valkeinen 		.source_pad = CSI2_PAD_FIRST_SOURCE,
387 *6edb685aSTomi Valkeinen 		.source_stream = 0,
388 *6edb685aSTomi Valkeinen 		.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
389 *6edb685aSTomi Valkeinen 	} };
390 *6edb685aSTomi Valkeinen 
391 *6edb685aSTomi Valkeinen 	struct v4l2_subdev_krouting routing = {
392 *6edb685aSTomi Valkeinen 		.num_routes = ARRAY_SIZE(routes),
393 *6edb685aSTomi Valkeinen 		.routes = routes,
394 *6edb685aSTomi Valkeinen 	};
395 *6edb685aSTomi Valkeinen 
396 *6edb685aSTomi Valkeinen 	int ret;
397 *6edb685aSTomi Valkeinen 
398 *6edb685aSTomi Valkeinen 	ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
399 *6edb685aSTomi Valkeinen 					       &cfe_default_format);
400 *6edb685aSTomi Valkeinen 	if (ret)
401 *6edb685aSTomi Valkeinen 		return ret;
402 *6edb685aSTomi Valkeinen 
403 *6edb685aSTomi Valkeinen 	return 0;
404 *6edb685aSTomi Valkeinen }
405 *6edb685aSTomi Valkeinen 
406 *6edb685aSTomi Valkeinen static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
407 *6edb685aSTomi Valkeinen 			    struct v4l2_subdev_state *state,
408 *6edb685aSTomi Valkeinen 			    struct v4l2_subdev_format *format)
409 *6edb685aSTomi Valkeinen {
410 *6edb685aSTomi Valkeinen 	if (format->pad == CSI2_PAD_SINK) {
411 *6edb685aSTomi Valkeinen 		/* Store the sink format and propagate it to the source. */
412 *6edb685aSTomi Valkeinen 
413 *6edb685aSTomi Valkeinen 		const struct cfe_fmt *cfe_fmt;
414 *6edb685aSTomi Valkeinen 
415 *6edb685aSTomi Valkeinen 		cfe_fmt = find_format_by_code(format->format.code);
416 *6edb685aSTomi Valkeinen 		if (!cfe_fmt) {
417 *6edb685aSTomi Valkeinen 			cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10);
418 *6edb685aSTomi Valkeinen 			format->format.code = cfe_fmt->code;
419 *6edb685aSTomi Valkeinen 		}
420 *6edb685aSTomi Valkeinen 
421 *6edb685aSTomi Valkeinen 		struct v4l2_mbus_framefmt *fmt;
422 *6edb685aSTomi Valkeinen 
423 *6edb685aSTomi Valkeinen 		fmt = v4l2_subdev_state_get_format(state, format->pad,
424 *6edb685aSTomi Valkeinen 						   format->stream);
425 *6edb685aSTomi Valkeinen 		if (!fmt)
426 *6edb685aSTomi Valkeinen 			return -EINVAL;
427 *6edb685aSTomi Valkeinen 
428 *6edb685aSTomi Valkeinen 		*fmt = format->format;
429 *6edb685aSTomi Valkeinen 
430 *6edb685aSTomi Valkeinen 		fmt = v4l2_subdev_state_get_opposite_stream_format(state,
431 *6edb685aSTomi Valkeinen 								   format->pad,
432 *6edb685aSTomi Valkeinen 								   format->stream);
433 *6edb685aSTomi Valkeinen 		if (!fmt)
434 *6edb685aSTomi Valkeinen 			return -EINVAL;
435 *6edb685aSTomi Valkeinen 
436 *6edb685aSTomi Valkeinen 		format->format.field = V4L2_FIELD_NONE;
437 *6edb685aSTomi Valkeinen 
438 *6edb685aSTomi Valkeinen 		*fmt = format->format;
439 *6edb685aSTomi Valkeinen 	} else {
440 *6edb685aSTomi Valkeinen 		/* Only allow changing the source pad mbus code. */
441 *6edb685aSTomi Valkeinen 
442 *6edb685aSTomi Valkeinen 		struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
443 *6edb685aSTomi Valkeinen 		u32 sink_code;
444 *6edb685aSTomi Valkeinen 		u32 code;
445 *6edb685aSTomi Valkeinen 
446 *6edb685aSTomi Valkeinen 		sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state,
447 *6edb685aSTomi Valkeinen 									format->pad,
448 *6edb685aSTomi Valkeinen 									format->stream);
449 *6edb685aSTomi Valkeinen 		if (!sink_fmt)
450 *6edb685aSTomi Valkeinen 			return -EINVAL;
451 *6edb685aSTomi Valkeinen 
452 *6edb685aSTomi Valkeinen 		source_fmt = v4l2_subdev_state_get_format(state, format->pad,
453 *6edb685aSTomi Valkeinen 							  format->stream);
454 *6edb685aSTomi Valkeinen 		if (!source_fmt)
455 *6edb685aSTomi Valkeinen 			return -EINVAL;
456 *6edb685aSTomi Valkeinen 
457 *6edb685aSTomi Valkeinen 		sink_code = sink_fmt->code;
458 *6edb685aSTomi Valkeinen 		code = format->format.code;
459 *6edb685aSTomi Valkeinen 
460 *6edb685aSTomi Valkeinen 		/*
461 *6edb685aSTomi Valkeinen 		 * Only allow changing the mbus code to:
462 *6edb685aSTomi Valkeinen 		 * - The sink's mbus code
463 *6edb685aSTomi Valkeinen 		 * - The 16-bit version of the sink's mbus code
464 *6edb685aSTomi Valkeinen 		 * - The compressed version of the sink's mbus code
465 *6edb685aSTomi Valkeinen 		 */
466 *6edb685aSTomi Valkeinen 		if (code == sink_code ||
467 *6edb685aSTomi Valkeinen 		    code == cfe_find_16bit_code(sink_code) ||
468 *6edb685aSTomi Valkeinen 		    code == cfe_find_compressed_code(sink_code))
469 *6edb685aSTomi Valkeinen 			source_fmt->code = code;
470 *6edb685aSTomi Valkeinen 
471 *6edb685aSTomi Valkeinen 		format->format.code = source_fmt->code;
472 *6edb685aSTomi Valkeinen 	}
473 *6edb685aSTomi Valkeinen 
474 *6edb685aSTomi Valkeinen 	return 0;
475 *6edb685aSTomi Valkeinen }
476 *6edb685aSTomi Valkeinen 
477 *6edb685aSTomi Valkeinen static int csi2_set_routing(struct v4l2_subdev *sd,
478 *6edb685aSTomi Valkeinen 			    struct v4l2_subdev_state *state,
479 *6edb685aSTomi Valkeinen 			    enum v4l2_subdev_format_whence which,
480 *6edb685aSTomi Valkeinen 			    struct v4l2_subdev_krouting *routing)
481 *6edb685aSTomi Valkeinen {
482 *6edb685aSTomi Valkeinen 	int ret;
483 *6edb685aSTomi Valkeinen 
484 *6edb685aSTomi Valkeinen 	ret = v4l2_subdev_routing_validate(sd, routing,
485 *6edb685aSTomi Valkeinen 					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
486 *6edb685aSTomi Valkeinen 					   V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
487 *6edb685aSTomi Valkeinen 	if (ret)
488 *6edb685aSTomi Valkeinen 		return ret;
489 *6edb685aSTomi Valkeinen 
490 *6edb685aSTomi Valkeinen 	/* Only stream ID 0 allowed on source pads */
491 *6edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < routing->num_routes; ++i) {
492 *6edb685aSTomi Valkeinen 		const struct v4l2_subdev_route *route = &routing->routes[i];
493 *6edb685aSTomi Valkeinen 
494 *6edb685aSTomi Valkeinen 		if (route->source_stream != 0)
495 *6edb685aSTomi Valkeinen 			return -EINVAL;
496 *6edb685aSTomi Valkeinen 	}
497 *6edb685aSTomi Valkeinen 
498 *6edb685aSTomi Valkeinen 	ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
499 *6edb685aSTomi Valkeinen 					       &cfe_default_format);
500 *6edb685aSTomi Valkeinen 	if (ret)
501 *6edb685aSTomi Valkeinen 		return ret;
502 *6edb685aSTomi Valkeinen 
503 *6edb685aSTomi Valkeinen 	return 0;
504 *6edb685aSTomi Valkeinen }
505 *6edb685aSTomi Valkeinen 
506 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
507 *6edb685aSTomi Valkeinen 	.get_fmt = v4l2_subdev_get_fmt,
508 *6edb685aSTomi Valkeinen 	.set_fmt = csi2_pad_set_fmt,
509 *6edb685aSTomi Valkeinen 	.set_routing = csi2_set_routing,
510 *6edb685aSTomi Valkeinen 	.link_validate = v4l2_subdev_link_validate_default,
511 *6edb685aSTomi Valkeinen };
512 *6edb685aSTomi Valkeinen 
513 *6edb685aSTomi Valkeinen static const struct media_entity_operations csi2_entity_ops = {
514 *6edb685aSTomi Valkeinen 	.link_validate = v4l2_subdev_link_validate,
515 *6edb685aSTomi Valkeinen 	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
516 *6edb685aSTomi Valkeinen };
517 *6edb685aSTomi Valkeinen 
518 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_ops csi2_subdev_ops = {
519 *6edb685aSTomi Valkeinen 	.pad = &csi2_subdev_pad_ops,
520 *6edb685aSTomi Valkeinen };
521 *6edb685aSTomi Valkeinen 
522 *6edb685aSTomi Valkeinen static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
523 *6edb685aSTomi Valkeinen 	.init_state = csi2_init_state,
524 *6edb685aSTomi Valkeinen };
525 *6edb685aSTomi Valkeinen 
526 *6edb685aSTomi Valkeinen int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
527 *6edb685aSTomi Valkeinen {
528 *6edb685aSTomi Valkeinen 	unsigned int ret;
529 *6edb685aSTomi Valkeinen 
530 *6edb685aSTomi Valkeinen 	spin_lock_init(&csi2->errors_lock);
531 *6edb685aSTomi Valkeinen 
532 *6edb685aSTomi Valkeinen 	csi2->dphy.dev = csi2->v4l2_dev->dev;
533 *6edb685aSTomi Valkeinen 	dphy_probe(&csi2->dphy);
534 *6edb685aSTomi Valkeinen 
535 *6edb685aSTomi Valkeinen 	debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops);
536 *6edb685aSTomi Valkeinen 
537 *6edb685aSTomi Valkeinen 	if (csi2_track_errors)
538 *6edb685aSTomi Valkeinen 		debugfs_create_file("csi2_errors", 0440, debugfs, csi2,
539 *6edb685aSTomi Valkeinen 				    &csi2_errors_fops);
540 *6edb685aSTomi Valkeinen 
541 *6edb685aSTomi Valkeinen 	csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
542 *6edb685aSTomi Valkeinen 
543 *6edb685aSTomi Valkeinen 	for (unsigned int i = CSI2_PAD_FIRST_SOURCE;
544 *6edb685aSTomi Valkeinen 	     i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++)
545 *6edb685aSTomi Valkeinen 		csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE;
546 *6edb685aSTomi Valkeinen 
547 *6edb685aSTomi Valkeinen 	ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),
548 *6edb685aSTomi Valkeinen 				     csi2->pad);
549 *6edb685aSTomi Valkeinen 	if (ret)
550 *6edb685aSTomi Valkeinen 		return ret;
551 *6edb685aSTomi Valkeinen 
552 *6edb685aSTomi Valkeinen 	/* Initialize subdev */
553 *6edb685aSTomi Valkeinen 	v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
554 *6edb685aSTomi Valkeinen 	csi2->sd.internal_ops = &csi2_internal_ops;
555 *6edb685aSTomi Valkeinen 	csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
556 *6edb685aSTomi Valkeinen 	csi2->sd.entity.ops = &csi2_entity_ops;
557 *6edb685aSTomi Valkeinen 	csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
558 *6edb685aSTomi Valkeinen 	csi2->sd.owner = THIS_MODULE;
559 *6edb685aSTomi Valkeinen 	snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");
560 *6edb685aSTomi Valkeinen 
561 *6edb685aSTomi Valkeinen 	ret = v4l2_subdev_init_finalize(&csi2->sd);
562 *6edb685aSTomi Valkeinen 	if (ret)
563 *6edb685aSTomi Valkeinen 		goto err_entity_cleanup;
564 *6edb685aSTomi Valkeinen 
565 *6edb685aSTomi Valkeinen 	ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);
566 *6edb685aSTomi Valkeinen 	if (ret) {
567 *6edb685aSTomi Valkeinen 		csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret);
568 *6edb685aSTomi Valkeinen 		goto err_subdev_cleanup;
569 *6edb685aSTomi Valkeinen 	}
570 *6edb685aSTomi Valkeinen 
571 *6edb685aSTomi Valkeinen 	return 0;
572 *6edb685aSTomi Valkeinen 
573 *6edb685aSTomi Valkeinen err_subdev_cleanup:
574 *6edb685aSTomi Valkeinen 	v4l2_subdev_cleanup(&csi2->sd);
575 *6edb685aSTomi Valkeinen err_entity_cleanup:
576 *6edb685aSTomi Valkeinen 	media_entity_cleanup(&csi2->sd.entity);
577 *6edb685aSTomi Valkeinen 
578 *6edb685aSTomi Valkeinen 	return ret;
579 *6edb685aSTomi Valkeinen }
580 *6edb685aSTomi Valkeinen 
581 *6edb685aSTomi Valkeinen void csi2_uninit(struct csi2_device *csi2)
582 *6edb685aSTomi Valkeinen {
583 *6edb685aSTomi Valkeinen 	v4l2_device_unregister_subdev(&csi2->sd);
584 *6edb685aSTomi Valkeinen 	v4l2_subdev_cleanup(&csi2->sd);
585 *6edb685aSTomi Valkeinen 	media_entity_cleanup(&csi2->sd.entity);
586 *6edb685aSTomi Valkeinen }
587