xref: /linux/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c (revision 9738280aae592b579a25b5b1b6584c894827d3c7)
1 // SPDX-License-Identifier: (GPL-2.0-only OR MIT)
2 /*
3  * Copyright (C) 2024 Amlogic, Inc. All rights reserved
4  */
5 
6 #include <linux/cleanup.h>
7 #include <linux/media/amlogic/c3-isp-config.h>
8 #include <linux/pm_runtime.h>
9 
10 #include <media/v4l2-ioctl.h>
11 #include <media/v4l2-mc.h>
12 #include <media/videobuf2-vmalloc.h>
13 
14 #include "c3-isp-common.h"
15 #include "c3-isp-regs.h"
16 
17 /*
18  * union c3_isp_params_block - Generalisation of a parameter block
19  *
20  * This union allows the driver to treat a block as a generic struct to this
21  * union and safely access the header and block-specific struct without having
22  * to resort to casting. The header member is accessed first, and the type field
23  * checked which allows the driver to determine which of the other members
24  * should be used.
25  *
26  * @header:		The shared header struct embedded as the first member
27  *			of all the possible other members. This member would be
28  *			accessed first and the type field checked to determine
29  *			which of the other members should be accessed.
30  * @awb_gains:		For header.type == C3_ISP_PARAMS_BLOCK_AWB_GAINS
31  * @awb_cfg:		For header.type == C3_ISP_PARAMS_BLOCK_AWB_CONFIG
32  * @ae_cfg:		For header.type == C3_ISP_PARAMS_BLOCK_AE_CONFIG
33  * @af_cfg:		For header.type == C3_ISP_PARAMS_BLOCK_AF_CONFIG
34  * @pst_gamma:		For header.type == C3_ISP_PARAMS_BLOCK_PST_GAMMA
35  * @ccm:		For header.type == C3_ISP_PARAMS_BLOCK_CCM
36  * @csc:		For header.type == C3_ISP_PARAMS_BLOCK_CSC
37  * @blc:		For header.type == C3_ISP_PARAMS_BLOCK_BLC
38  */
39 union c3_isp_params_block {
40 	struct c3_isp_params_block_header header;
41 	struct c3_isp_params_awb_gains awb_gains;
42 	struct c3_isp_params_awb_config awb_cfg;
43 	struct c3_isp_params_ae_config ae_cfg;
44 	struct c3_isp_params_af_config af_cfg;
45 	struct c3_isp_params_pst_gamma pst_gamma;
46 	struct c3_isp_params_ccm ccm;
47 	struct c3_isp_params_csc csc;
48 	struct c3_isp_params_blc blc;
49 };
50 
51 typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp,
52 				     const union c3_isp_params_block *block);
53 
54 struct c3_isp_params_handler {
55 	size_t size;
56 	c3_isp_block_handler handler;
57 };
58 
59 #define to_c3_isp_params_buffer(vbuf) \
60 	container_of(vbuf, struct c3_isp_params_buffer, vb)
61 
62 /* Hardware configuration */
63 
64 static void c3_isp_params_cfg_awb_gains(struct c3_isp_device *isp,
65 					const union c3_isp_params_block *block)
66 {
67 	const struct c3_isp_params_awb_gains *awb_gains = &block->awb_gains;
68 
69 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
70 		c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
71 				   ISP_TOP_BEO_CTRL_WB_EN_MASK,
72 				   ISP_TOP_BEO_CTRL_WB_DIS);
73 		return;
74 	}
75 
76 	c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
77 			   ISP_LSWB_WB_GAIN0_GR_GAIN_MASK,
78 			   ISP_LSWB_WB_GAIN0_GR_GAIN(awb_gains->gr_gain));
79 	c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
80 			   ISP_LSWB_WB_GAIN0_R_GAIN_MASK,
81 			   ISP_LSWB_WB_GAIN0_R_GAIN(awb_gains->r_gain));
82 	c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
83 			   ISP_LSWB_WB_GAIN1_B_GAIN_MASK,
84 			   ISP_LSWB_WB_GAIN1_B_GAIN(awb_gains->b_gain));
85 	c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
86 			   ISP_LSWB_WB_GAIN1_GB_GAIN_MASK,
87 			   ISP_LSWB_WB_GAIN1_GB_GAIN(awb_gains->gb_gain));
88 	c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN2,
89 			   ISP_LSWB_WB_GAIN2_IR_GAIN_MASK,
90 			   ISP_LSWB_WB_GAIN2_IR_GAIN(awb_gains->gb_gain));
91 
92 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
93 		c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
94 				   ISP_TOP_BEO_CTRL_WB_EN_MASK,
95 				   ISP_TOP_BEO_CTRL_WB_EN);
96 }
97 
98 static void c3_isp_params_awb_wt(struct c3_isp_device *isp,
99 				 const struct c3_isp_params_awb_config *cfg)
100 {
101 	unsigned int zones_num;
102 	unsigned int base;
103 	unsigned int data;
104 	unsigned int i;
105 
106 	/* Set the weight address to 0 position */
107 	c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);
108 
109 	zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
110 
111 	/* Need to write 8 weights at once */
112 	for (i = 0; i < zones_num / 8; i++) {
113 		base = i * 8;
114 		data = ISP_AWB_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
115 		       ISP_AWB_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
116 		       ISP_AWB_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
117 		       ISP_AWB_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
118 		       ISP_AWB_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
119 		       ISP_AWB_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
120 		       ISP_AWB_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
121 		       ISP_AWB_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
122 		c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
123 	}
124 
125 	if (zones_num % 8 == 0)
126 		return;
127 
128 	data = 0;
129 	base = i * 8;
130 
131 	for (i = 0; i < zones_num % 8; i++)
132 		data |= ISP_AWB_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
133 
134 	c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
135 }
136 
137 static void c3_isp_params_awb_cood(struct c3_isp_device *isp,
138 				   const struct c3_isp_params_awb_config *cfg)
139 {
140 	unsigned int max_point_num;
141 
142 	/* The number of points is one more than the number of edges */
143 	max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
144 
145 	/* Set the index address to 0 position */
146 	c3_isp_write(isp, ISP_AWB_IDX_ADDR, 0);
147 
148 	for (unsigned int i = 0; i < max_point_num; i++)
149 		c3_isp_write(isp, ISP_AWB_IDX_DATA,
150 			     ISP_AWB_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
151 			     ISP_AWB_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
152 }
153 
154 static void c3_isp_params_cfg_awb_config(struct c3_isp_device *isp,
155 					 const union c3_isp_params_block *block)
156 {
157 	const struct c3_isp_params_awb_config *awb_cfg = &block->awb_cfg;
158 
159 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
160 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
161 				   ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
162 				   ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
163 		return;
164 	}
165 
166 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
167 			   ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK,
168 			   ISP_TOP_3A_STAT_CRTL_AWB_POINT(awb_cfg->tap_point));
169 
170 	c3_isp_update_bits(isp, ISP_AWB_STAT_CTRL2,
171 			   ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK,
172 			   ISP_AWB_STAT_CTRL2_SATUR_CTRL(awb_cfg->satur_vald));
173 
174 	c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
175 			   ISP_AWB_HV_BLKNUM_H_NUM_MASK,
176 			   ISP_AWB_HV_BLKNUM_H_NUM(awb_cfg->horiz_zones_num));
177 	c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
178 			   ISP_AWB_HV_BLKNUM_V_NUM_MASK,
179 			   ISP_AWB_HV_BLKNUM_V_NUM(awb_cfg->vert_zones_num));
180 
181 	c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MIN_VALUE_MASK,
182 			   ISP_AWB_STAT_RG_MIN_VALUE(awb_cfg->rg_min));
183 	c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MAX_VALUE_MASK,
184 			   ISP_AWB_STAT_RG_MAX_VALUE(awb_cfg->rg_max));
185 
186 	c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MIN_VALUE_MASK,
187 			   ISP_AWB_STAT_BG_MIN_VALUE(awb_cfg->bg_min));
188 	c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MAX_VALUE_MASK,
189 			   ISP_AWB_STAT_BG_MAX_VALUE(awb_cfg->bg_max));
190 
191 	c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
192 			   ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK,
193 			   ISP_AWB_STAT_RG_HL_LOW_VALUE(awb_cfg->rg_low));
194 	c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
195 			   ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK,
196 			   ISP_AWB_STAT_RG_HL_HIGH_VALUE(awb_cfg->rg_high));
197 
198 	c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
199 			   ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK,
200 			   ISP_AWB_STAT_BG_HL_LOW_VALUE(awb_cfg->bg_low));
201 	c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
202 			   ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK,
203 			   ISP_AWB_STAT_BG_HL_HIGH_VALUE(awb_cfg->bg_high));
204 
205 	c3_isp_params_awb_wt(isp, awb_cfg);
206 	c3_isp_params_awb_cood(isp, awb_cfg);
207 
208 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
209 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
210 				   ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
211 				   ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN);
212 }
213 
214 static void c3_isp_params_ae_wt(struct c3_isp_device *isp,
215 				const struct c3_isp_params_ae_config *cfg)
216 {
217 	unsigned int zones_num;
218 	unsigned int base;
219 	unsigned int data;
220 	unsigned int i;
221 
222 	/* Set the weight address to 0 position */
223 	c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);
224 
225 	zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
226 
227 	/* Need to write 8 weights at once */
228 	for (i = 0; i < zones_num / 8; i++) {
229 		base = i * 8;
230 		data = ISP_AE_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
231 		       ISP_AE_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
232 		       ISP_AE_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
233 		       ISP_AE_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
234 		       ISP_AE_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
235 		       ISP_AE_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
236 		       ISP_AE_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
237 		       ISP_AE_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
238 		c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
239 	}
240 
241 	if (zones_num % 8 == 0)
242 		return;
243 
244 	data = 0;
245 	base = i * 8;
246 
247 	/* Write the last weights data */
248 	for (i = 0; i < zones_num % 8; i++)
249 		data |= ISP_AE_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
250 
251 	c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
252 }
253 
254 static void c3_isp_params_ae_cood(struct c3_isp_device *isp,
255 				  const struct c3_isp_params_ae_config *cfg)
256 {
257 	unsigned int max_point_num;
258 
259 	/* The number of points is one more than the number of edges */
260 	max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
261 
262 	/* Set the index address to 0 position */
263 	c3_isp_write(isp, ISP_AE_IDX_ADDR, 0);
264 
265 	for (unsigned int i = 0; i < max_point_num; i++)
266 		c3_isp_write(isp, ISP_AE_IDX_DATA,
267 			     ISP_AE_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
268 			     ISP_AE_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
269 }
270 
271 static void c3_isp_params_cfg_ae_config(struct c3_isp_device *isp,
272 					const union c3_isp_params_block *block)
273 {
274 	const struct c3_isp_params_ae_config *ae_cfg = &block->ae_cfg;
275 
276 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
277 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
278 				   ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
279 				   ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
280 		return;
281 	}
282 
283 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
284 			   ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK,
285 			   ISP_TOP_3A_STAT_CRTL_AE_POINT(ae_cfg->tap_point));
286 
287 	if (ae_cfg->tap_point == C3_ISP_AE_STATS_TAP_GE)
288 		c3_isp_update_bits(isp, ISP_AE_CTRL,
289 				   ISP_AE_CTRL_INPUT_2LINE_MASK,
290 				   ISP_AE_CTRL_INPUT_2LINE_EN);
291 	else
292 		c3_isp_update_bits(isp, ISP_AE_CTRL,
293 				   ISP_AE_CTRL_INPUT_2LINE_MASK,
294 				   ISP_AE_CTRL_INPUT_2LINE_DIS);
295 
296 	c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
297 			   ISP_AE_HV_BLKNUM_H_NUM_MASK,
298 			   ISP_AE_HV_BLKNUM_H_NUM(ae_cfg->horiz_zones_num));
299 	c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
300 			   ISP_AE_HV_BLKNUM_V_NUM_MASK,
301 			   ISP_AE_HV_BLKNUM_V_NUM(ae_cfg->vert_zones_num));
302 
303 	c3_isp_params_ae_wt(isp, ae_cfg);
304 	c3_isp_params_ae_cood(isp, ae_cfg);
305 
306 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
307 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
308 				   ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
309 				   ISP_TOP_3A_STAT_CRTL_AE_STAT_EN);
310 }
311 
312 static void c3_isp_params_af_cood(struct c3_isp_device *isp,
313 				  const struct c3_isp_params_af_config *cfg)
314 {
315 	unsigned int max_point_num;
316 
317 	/* The number of points is one more than the number of edges */
318 	max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
319 
320 	/* Set the index address to 0 position */
321 	c3_isp_write(isp, ISP_AF_IDX_ADDR, 0);
322 
323 	for (unsigned int i = 0; i < max_point_num; i++)
324 		c3_isp_write(isp, ISP_AF_IDX_DATA,
325 			     ISP_AF_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
326 			     ISP_AF_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
327 }
328 
329 static void c3_isp_params_cfg_af_config(struct c3_isp_device *isp,
330 					const union c3_isp_params_block *block)
331 {
332 	const struct c3_isp_params_af_config *af_cfg = &block->af_cfg;
333 
334 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
335 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
336 				   ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
337 				   ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
338 		return;
339 	}
340 
341 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
342 			   ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK,
343 			   ISP_TOP_3A_STAT_CRTL_AF_POINT(af_cfg->tap_point));
344 
345 	c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
346 			   ISP_AF_HV_BLKNUM_H_NUM_MASK,
347 			   ISP_AF_HV_BLKNUM_H_NUM(af_cfg->horiz_zones_num));
348 	c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
349 			   ISP_AF_HV_BLKNUM_V_NUM_MASK,
350 			   ISP_AF_HV_BLKNUM_V_NUM(af_cfg->vert_zones_num));
351 
352 	c3_isp_params_af_cood(isp, af_cfg);
353 
354 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
355 		c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
356 				   ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
357 				   ISP_TOP_3A_STAT_CRTL_AF_STAT_EN);
358 }
359 
360 static void c3_isp_params_cfg_pst_gamma(struct c3_isp_device *isp,
361 					const union c3_isp_params_block *block)
362 {
363 	const struct c3_isp_params_pst_gamma *gm = &block->pst_gamma;
364 	unsigned int base;
365 	unsigned int i;
366 
367 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
368 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
369 				   ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
370 				   ISP_TOP_BED_CTRL_PST_GAMMA_DIS);
371 		return;
372 	}
373 
374 	/* R, G and B channels use the same gamma lut */
375 	for (unsigned int j = 0; j < 3; j++) {
376 		/* Set the channel lut address */
377 		c3_isp_write(isp, ISP_PST_GAMMA_LUT_ADDR,
378 			     ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(j));
379 
380 		/* Need to write 2 lut values at once */
381 		for (i = 0; i < ARRAY_SIZE(gm->lut) / 2; i++) {
382 			base = i * 2;
383 			c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
384 				     ISP_PST_GM_LUT_DATA0(gm->lut[base]) |
385 				     ISP_PST_GM_LUT_DATA1(gm->lut[base + 1]));
386 		}
387 
388 		/* Write the last one */
389 		if (ARRAY_SIZE(gm->lut) % 2) {
390 			base = i * 2;
391 			c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
392 				     ISP_PST_GM_LUT_DATA0(gm->lut[base]));
393 		}
394 	}
395 
396 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
397 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
398 				   ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
399 				   ISP_TOP_BED_CTRL_PST_GAMMA_EN);
400 }
401 
402 /* Configure 3 x 3 ccm matrix */
403 static void c3_isp_params_cfg_ccm(struct c3_isp_device *isp,
404 				  const union c3_isp_params_block *block)
405 {
406 	const struct c3_isp_params_ccm *ccm = &block->ccm;
407 
408 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
409 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
410 				   ISP_TOP_BED_CTRL_CCM_EN_MASK,
411 				   ISP_TOP_BED_CTRL_CCM_DIS);
412 		return;
413 	}
414 
415 	c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
416 			   ISP_CCM_MTX_00_01_MTX_00_MASK,
417 			   ISP_CCM_MTX_00_01_MTX_00(ccm->matrix[0][0]));
418 	c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
419 			   ISP_CCM_MTX_00_01_MTX_01_MASK,
420 			   ISP_CCM_MTX_00_01_MTX_01(ccm->matrix[0][1]));
421 	c3_isp_update_bits(isp, ISP_CCM_MTX_02_03,
422 			   ISP_CCM_MTX_02_03_MTX_02_MASK,
423 			   ISP_CCM_MTX_02_03_MTX_02(ccm->matrix[0][2]));
424 
425 	c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
426 			   ISP_CCM_MTX_10_11_MTX_10_MASK,
427 			   ISP_CCM_MTX_10_11_MTX_10(ccm->matrix[1][0]));
428 	c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
429 			   ISP_CCM_MTX_10_11_MTX_11_MASK,
430 			   ISP_CCM_MTX_10_11_MTX_11(ccm->matrix[1][1]));
431 	c3_isp_update_bits(isp, ISP_CCM_MTX_12_13,
432 			   ISP_CCM_MTX_12_13_MTX_12_MASK,
433 			   ISP_CCM_MTX_12_13_MTX_12(ccm->matrix[1][2]));
434 
435 	c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
436 			   ISP_CCM_MTX_20_21_MTX_20_MASK,
437 			   ISP_CCM_MTX_20_21_MTX_20(ccm->matrix[2][0]));
438 	c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
439 			   ISP_CCM_MTX_20_21_MTX_21_MASK,
440 			   ISP_CCM_MTX_20_21_MTX_21(ccm->matrix[2][1]));
441 	c3_isp_update_bits(isp, ISP_CCM_MTX_22_23_RS,
442 			   ISP_CCM_MTX_22_23_RS_MTX_22_MASK,
443 			   ISP_CCM_MTX_22_23_RS_MTX_22(ccm->matrix[2][2]));
444 
445 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
446 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
447 				   ISP_TOP_BED_CTRL_CCM_EN_MASK,
448 				   ISP_TOP_BED_CTRL_CCM_EN);
449 }
450 
451 /* Configure color space conversion matrix parameters */
452 static void c3_isp_params_cfg_csc(struct c3_isp_device *isp,
453 				  const union c3_isp_params_block *block)
454 {
455 	const struct c3_isp_params_csc *csc = &block->csc;
456 
457 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
458 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
459 				   ISP_TOP_BED_CTRL_CM0_EN_MASK,
460 				   ISP_TOP_BED_CTRL_CM0_DIS);
461 		return;
462 	}
463 
464 	c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
465 			   ISP_CM0_COEF00_01_MTX_00_MASK,
466 			   ISP_CM0_COEF00_01_MTX_00(csc->matrix[0][0]));
467 	c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
468 			   ISP_CM0_COEF00_01_MTX_01_MASK,
469 			   ISP_CM0_COEF00_01_MTX_01(csc->matrix[0][1]));
470 	c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
471 			   ISP_CM0_COEF02_10_MTX_02_MASK,
472 			   ISP_CM0_COEF02_10_MTX_02(csc->matrix[0][2]));
473 
474 	c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
475 			   ISP_CM0_COEF02_10_MTX_10_MASK,
476 			   ISP_CM0_COEF02_10_MTX_10(csc->matrix[1][0]));
477 	c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
478 			   ISP_CM0_COEF11_12_MTX_11_MASK,
479 			   ISP_CM0_COEF11_12_MTX_11(csc->matrix[1][1]));
480 	c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
481 			   ISP_CM0_COEF11_12_MTX_12_MASK,
482 			   ISP_CM0_COEF11_12_MTX_12(csc->matrix[1][2]));
483 
484 	c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
485 			   ISP_CM0_COEF20_21_MTX_20_MASK,
486 			   ISP_CM0_COEF20_21_MTX_20(csc->matrix[2][0]));
487 	c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
488 			   ISP_CM0_COEF20_21_MTX_21_MASK,
489 			   ISP_CM0_COEF20_21_MTX_21(csc->matrix[2][1]));
490 	c3_isp_update_bits(isp, ISP_CM0_COEF22_OUP_OFST0,
491 			   ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK,
492 			   ISP_CM0_COEF22_OUP_OFST0_MTX_22(csc->matrix[2][2]));
493 
494 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
495 		c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
496 				   ISP_TOP_BED_CTRL_CM0_EN_MASK,
497 				   ISP_TOP_BED_CTRL_CM0_EN);
498 }
499 
500 /* Set blc offset of each color channel */
501 static void c3_isp_params_cfg_blc(struct c3_isp_device *isp,
502 				  const union c3_isp_params_block *block)
503 {
504 	const struct c3_isp_params_blc *blc = &block->blc;
505 
506 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
507 		c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
508 				   ISP_TOP_BEO_CTRL_BLC_EN_MASK,
509 				   ISP_TOP_BEO_CTRL_BLC_DIS);
510 		return;
511 	}
512 
513 	c3_isp_write(isp, ISP_LSWB_BLC_OFST0,
514 		     ISP_LSWB_BLC_OFST0_R_OFST(blc->r_ofst) |
515 		     ISP_LSWB_BLC_OFST0_GR_OFST(blc->gr_ofst));
516 	c3_isp_write(isp, ISP_LSWB_BLC_OFST1,
517 		     ISP_LSWB_BLC_OFST1_GB_OFST(blc->gb_ofst) |
518 		     ISP_LSWB_BLC_OFST1_B_OFST(blc->b_ofst));
519 
520 	if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
521 		c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
522 				   ISP_TOP_BEO_CTRL_BLC_EN_MASK,
523 				   ISP_TOP_BEO_CTRL_BLC_EN);
524 }
525 
526 static const struct c3_isp_params_handler c3_isp_params_handlers[] = {
527 	[C3_ISP_PARAMS_BLOCK_AWB_GAINS] = {
528 		.size = sizeof(struct c3_isp_params_awb_gains),
529 		.handler = c3_isp_params_cfg_awb_gains,
530 	},
531 	[C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = {
532 		.size = sizeof(struct c3_isp_params_awb_config),
533 		.handler = c3_isp_params_cfg_awb_config,
534 	},
535 	[C3_ISP_PARAMS_BLOCK_AE_CONFIG] = {
536 		.size = sizeof(struct c3_isp_params_ae_config),
537 		.handler = c3_isp_params_cfg_ae_config,
538 	},
539 	[C3_ISP_PARAMS_BLOCK_AF_CONFIG] = {
540 		.size = sizeof(struct c3_isp_params_af_config),
541 		.handler = c3_isp_params_cfg_af_config,
542 	},
543 	[C3_ISP_PARAMS_BLOCK_PST_GAMMA] = {
544 		.size = sizeof(struct c3_isp_params_pst_gamma),
545 		.handler = c3_isp_params_cfg_pst_gamma,
546 	},
547 	[C3_ISP_PARAMS_BLOCK_CCM] = {
548 		.size = sizeof(struct c3_isp_params_ccm),
549 		.handler = c3_isp_params_cfg_ccm,
550 	},
551 	[C3_ISP_PARAMS_BLOCK_CSC] = {
552 		.size = sizeof(struct c3_isp_params_csc),
553 		.handler = c3_isp_params_cfg_csc,
554 	},
555 	[C3_ISP_PARAMS_BLOCK_BLC] = {
556 		.size = sizeof(struct c3_isp_params_blc),
557 		.handler = c3_isp_params_cfg_blc,
558 	},
559 };
560 
561 static void c3_isp_params_cfg_blocks(struct c3_isp_params *params)
562 {
563 	struct c3_isp_params_cfg *config = params->buff->cfg;
564 	size_t block_offset = 0;
565 
566 	if (WARN_ON(!config))
567 		return;
568 
569 	/* Walk the list of parameter blocks and process them */
570 	while (block_offset < config->data_size) {
571 		const struct c3_isp_params_handler *block_handler;
572 		const union c3_isp_params_block *block;
573 
574 		block = (const union c3_isp_params_block *)
575 			 &config->data[block_offset];
576 
577 		block_handler = &c3_isp_params_handlers[block->header.type];
578 		block_handler->handler(params->isp, block);
579 
580 		block_offset += block->header.size;
581 	}
582 }
583 
584 void c3_isp_params_pre_cfg(struct c3_isp_device *isp)
585 {
586 	struct c3_isp_params *params = &isp->params;
587 
588 	/* Disable some unused modules */
589 	c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL0,
590 			   ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK,
591 			   ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS);
592 
593 	c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
594 			   ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK,
595 			   ISP_TOP_FEO_CTRL1_0_DPC_DIS);
596 	c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
597 			   ISP_TOP_FEO_CTRL1_0_OG_EN_MASK,
598 			   ISP_TOP_FEO_CTRL1_0_OG_DIS);
599 
600 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_PDPC_EN_MASK,
601 			   ISP_TOP_FED_CTRL_PDPC_DIS);
602 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
603 			   ISP_TOP_FED_CTRL_RAWCNR_EN_MASK,
604 			   ISP_TOP_FED_CTRL_RAWCNR_DIS);
605 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SNR1_EN_MASK,
606 			   ISP_TOP_FED_CTRL_SNR1_DIS);
607 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_TNR0_EN_MASK,
608 			   ISP_TOP_FED_CTRL_TNR0_DIS);
609 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
610 			   ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK,
611 			   ISP_TOP_FED_CTRL_CUBIC_CS_DIS);
612 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SQRT_EN_MASK,
613 			   ISP_TOP_FED_CTRL_SQRT_DIS);
614 	c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
615 			   ISP_TOP_FED_CTRL_DGAIN_EN_MASK,
616 			   ISP_TOP_FED_CTRL_DGAIN_DIS);
617 
618 	c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
619 			   ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK,
620 			   ISP_TOP_BEO_CTRL_INV_DGAIN_DIS);
621 	c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, ISP_TOP_BEO_CTRL_EOTF_EN_MASK,
622 			   ISP_TOP_BEO_CTRL_EOTF_DIS);
623 
624 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
625 			   ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK,
626 			   ISP_TOP_BED_CTRL_YHS_STAT_DIS);
627 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
628 			   ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK,
629 			   ISP_TOP_BED_CTRL_GRPH_STAT_DIS);
630 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
631 			   ISP_TOP_BED_CTRL_FMETER_EN_MASK,
632 			   ISP_TOP_BED_CTRL_FMETER_DIS);
633 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_BSC_EN_MASK,
634 			   ISP_TOP_BED_CTRL_BSC_DIS);
635 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CNR2_EN_MASK,
636 			   ISP_TOP_BED_CTRL_CNR2_DIS);
637 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CM1_EN_MASK,
638 			   ISP_TOP_BED_CTRL_CM1_DIS);
639 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
640 			   ISP_TOP_BED_CTRL_LUT3D_EN_MASK,
641 			   ISP_TOP_BED_CTRL_LUT3D_DIS);
642 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
643 			   ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK,
644 			   ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS);
645 	c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_AMCM_EN_MASK,
646 			   ISP_TOP_BED_CTRL_AMCM_DIS);
647 
648 	/*
649 	 * Disable AE, AF and AWB stat module. Please configure the parameters
650 	 * in userspace algorithm if need to enable these switch.
651 	 */
652 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
653 			   ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
654 			   ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
655 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
656 			   ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
657 			   ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
658 	c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
659 			   ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
660 			   ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
661 
662 	c3_isp_write(isp, ISP_LSWB_WB_LIMIT0,
663 		     ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX |
664 		     ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX);
665 	c3_isp_write(isp, ISP_LSWB_WB_LIMIT1,
666 		     ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX |
667 		     ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX);
668 
669 	guard(spinlock_irqsave)(&params->buff_lock);
670 
671 	/* Only use the first buffer to initialize ISP */
672 	params->buff =
673 		list_first_entry_or_null(&params->pending,
674 					 struct c3_isp_params_buffer, list);
675 	if (params->buff)
676 		c3_isp_params_cfg_blocks(params);
677 }
678 
679 /* V4L2 video operations */
680 
681 static int c3_isp_params_querycap(struct file *file, void *fh,
682 				  struct v4l2_capability *cap)
683 {
684 	strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
685 	strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
686 
687 	return 0;
688 }
689 
690 static int c3_isp_params_enum_fmt(struct file *file, void *fh,
691 				  struct v4l2_fmtdesc *f)
692 {
693 	if (f->index)
694 		return -EINVAL;
695 
696 	f->pixelformat = V4L2_META_FMT_C3ISP_PARAMS;
697 
698 	return 0;
699 }
700 
701 static int c3_isp_params_g_fmt(struct file *file, void *fh,
702 			       struct v4l2_format *f)
703 {
704 	struct c3_isp_params *params = video_drvdata(file);
705 
706 	f->fmt.meta = params->vfmt.fmt.meta;
707 
708 	return 0;
709 }
710 
711 static const struct v4l2_ioctl_ops isp_params_v4l2_ioctl_ops = {
712 	.vidioc_querycap                = c3_isp_params_querycap,
713 	.vidioc_enum_fmt_meta_out       = c3_isp_params_enum_fmt,
714 	.vidioc_g_fmt_meta_out          = c3_isp_params_g_fmt,
715 	.vidioc_s_fmt_meta_out          = c3_isp_params_g_fmt,
716 	.vidioc_try_fmt_meta_out        = c3_isp_params_g_fmt,
717 	.vidioc_reqbufs                 = vb2_ioctl_reqbufs,
718 	.vidioc_querybuf                = vb2_ioctl_querybuf,
719 	.vidioc_qbuf                    = vb2_ioctl_qbuf,
720 	.vidioc_expbuf                  = vb2_ioctl_expbuf,
721 	.vidioc_dqbuf                   = vb2_ioctl_dqbuf,
722 	.vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
723 	.vidioc_create_bufs             = vb2_ioctl_create_bufs,
724 	.vidioc_streamon                = vb2_ioctl_streamon,
725 	.vidioc_streamoff               = vb2_ioctl_streamoff,
726 };
727 
728 static const struct v4l2_file_operations isp_params_v4l2_fops = {
729 	.open = v4l2_fh_open,
730 	.release = vb2_fop_release,
731 	.poll = vb2_fop_poll,
732 	.unlocked_ioctl = video_ioctl2,
733 	.mmap = vb2_fop_mmap,
734 };
735 
736 static int c3_isp_params_vb2_queue_setup(struct vb2_queue *q,
737 					 unsigned int *num_buffers,
738 					 unsigned int *num_planes,
739 					 unsigned int sizes[],
740 					 struct device *alloc_devs[])
741 {
742 	if (*num_planes) {
743 		if (*num_planes != 1)
744 			return -EINVAL;
745 
746 		if (sizes[0] < sizeof(struct c3_isp_params_cfg))
747 			return -EINVAL;
748 
749 		return 0;
750 	}
751 
752 	*num_planes = 1;
753 	sizes[0] = sizeof(struct c3_isp_params_cfg);
754 
755 	return 0;
756 }
757 
758 static void c3_isp_params_vb2_buf_queue(struct vb2_buffer *vb)
759 {
760 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
761 	struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
762 	struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
763 
764 	guard(spinlock_irqsave)(&params->buff_lock);
765 
766 	list_add_tail(&buf->list, &params->pending);
767 }
768 
769 static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb)
770 {
771 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
772 	struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf);
773 	struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
774 	struct c3_isp_params_cfg *cfg = buf->cfg;
775 	struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0);
776 	size_t payload_size = vb2_get_plane_payload(vb, 0);
777 	size_t header_size = offsetof(struct c3_isp_params_cfg, data);
778 	size_t block_offset = 0;
779 	size_t cfg_size;
780 
781 	/* Payload size can't be greater than the destination buffer size */
782 	if (payload_size > params->vfmt.fmt.meta.buffersize) {
783 		dev_dbg(params->isp->dev,
784 			"Payload size is too large: %zu\n", payload_size);
785 		return -EINVAL;
786 	}
787 
788 	/* Payload size can't be smaller than the header size */
789 	if (payload_size < header_size) {
790 		dev_dbg(params->isp->dev,
791 			"Payload size is too small: %zu\n", payload_size);
792 		return -EINVAL;
793 	}
794 
795 	/*
796 	 * Use the internal scratch buffer to avoid userspace modifying
797 	 * the buffer content while the driver is processing it.
798 	 */
799 	memcpy(cfg, usr_cfg, payload_size);
800 
801 	/* Only v0 is supported at the moment */
802 	if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) {
803 		dev_dbg(params->isp->dev,
804 			"Invalid params buffer version: %u\n", cfg->version);
805 		return -EINVAL;
806 	}
807 
808 	/* Validate the size reported in the parameter buffer header */
809 	cfg_size = header_size + cfg->data_size;
810 	if (cfg_size != payload_size) {
811 		dev_dbg(params->isp->dev,
812 			"Data size %zu and payload size %zu are different\n",
813 			cfg_size, payload_size);
814 		return -EINVAL;
815 	}
816 
817 	/* Walk the list of parameter blocks and validate them */
818 	cfg_size = cfg->data_size;
819 	while (cfg_size >= sizeof(struct c3_isp_params_block_header)) {
820 		const struct c3_isp_params_block_header *block;
821 		const struct c3_isp_params_handler *handler;
822 
823 		block = (struct c3_isp_params_block_header *)
824 			&cfg->data[block_offset];
825 
826 		if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) {
827 			dev_dbg(params->isp->dev,
828 				"Invalid params block type\n");
829 			return -EINVAL;
830 		}
831 
832 		if (block->size > cfg_size) {
833 			dev_dbg(params->isp->dev,
834 				"Block size is greater than cfg size\n");
835 			return -EINVAL;
836 		}
837 
838 		if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
839 				     C3_ISP_PARAMS_BLOCK_FL_DISABLE)) ==
840 		    (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
841 		     C3_ISP_PARAMS_BLOCK_FL_DISABLE)) {
842 			dev_dbg(params->isp->dev,
843 				"Invalid parameters block flags\n");
844 			return -EINVAL;
845 		}
846 
847 		handler = &c3_isp_params_handlers[block->type];
848 		if (block->size != handler->size) {
849 			dev_dbg(params->isp->dev,
850 				"Invalid params block size\n");
851 			return -EINVAL;
852 		}
853 
854 		block_offset += block->size;
855 		cfg_size -= block->size;
856 	}
857 
858 	if (cfg_size) {
859 		dev_dbg(params->isp->dev,
860 			"Unexpected data after the params buffer end\n");
861 		return -EINVAL;
862 	}
863 
864 	return 0;
865 }
866 
867 static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb)
868 {
869 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
870 	struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
871 	struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
872 
873 	buf->cfg = kvmalloc(params->vfmt.fmt.meta.buffersize, GFP_KERNEL);
874 	if (!buf->cfg)
875 		return -ENOMEM;
876 
877 	return 0;
878 }
879 
880 static void c3_isp_params_vb2_buf_cleanup(struct vb2_buffer *vb)
881 {
882 	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
883 	struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
884 
885 	kvfree(buf->cfg);
886 	buf->cfg = NULL;
887 }
888 
889 static void c3_isp_params_vb2_stop_streaming(struct vb2_queue *q)
890 {
891 	struct c3_isp_params *params = vb2_get_drv_priv(q);
892 	struct c3_isp_params_buffer *buff;
893 
894 	guard(spinlock_irqsave)(&params->buff_lock);
895 
896 	while (!list_empty(&params->pending)) {
897 		buff = list_first_entry(&params->pending,
898 					struct c3_isp_params_buffer, list);
899 		list_del(&buff->list);
900 		vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
901 	}
902 }
903 
904 static const struct vb2_ops isp_params_vb2_ops = {
905 	.queue_setup = c3_isp_params_vb2_queue_setup,
906 	.buf_queue = c3_isp_params_vb2_buf_queue,
907 	.buf_prepare = c3_isp_params_vb2_buf_prepare,
908 	.buf_init = c3_isp_params_vb2_buf_init,
909 	.buf_cleanup = c3_isp_params_vb2_buf_cleanup,
910 	.stop_streaming = c3_isp_params_vb2_stop_streaming,
911 };
912 
913 int c3_isp_params_register(struct c3_isp_device *isp)
914 {
915 	struct c3_isp_params *params = &isp->params;
916 	struct video_device *vdev = &params->vdev;
917 	struct vb2_queue *vb2_q = &params->vb2_q;
918 	int ret;
919 
920 	memset(params, 0, sizeof(*params));
921 	params->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_PARAMS;
922 	params->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_params_cfg);
923 	params->isp = isp;
924 	INIT_LIST_HEAD(&params->pending);
925 	spin_lock_init(&params->buff_lock);
926 	mutex_init(&params->lock);
927 
928 	snprintf(vdev->name, sizeof(vdev->name), "c3-isp-params");
929 	vdev->fops = &isp_params_v4l2_fops;
930 	vdev->ioctl_ops = &isp_params_v4l2_ioctl_ops;
931 	vdev->v4l2_dev = &isp->v4l2_dev;
932 	vdev->lock = &params->lock;
933 	vdev->minor = -1;
934 	vdev->queue = vb2_q;
935 	vdev->release = video_device_release_empty;
936 	vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING;
937 	vdev->vfl_dir = VFL_DIR_TX;
938 	video_set_drvdata(vdev, params);
939 
940 	vb2_q->drv_priv = params;
941 	vb2_q->mem_ops = &vb2_vmalloc_memops;
942 	vb2_q->ops = &isp_params_vb2_ops;
943 	vb2_q->type = V4L2_BUF_TYPE_META_OUTPUT;
944 	vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
945 	vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
946 	vb2_q->buf_struct_size = sizeof(struct c3_isp_params_buffer);
947 	vb2_q->dev = isp->dev;
948 	vb2_q->lock = &params->lock;
949 	vb2_q->min_queued_buffers = 1;
950 
951 	ret = vb2_queue_init(vb2_q);
952 	if (ret)
953 		goto err_detroy;
954 
955 	params->pad.flags = MEDIA_PAD_FL_SOURCE;
956 	ret = media_entity_pads_init(&vdev->entity, 1, &params->pad);
957 	if (ret)
958 		goto err_queue_release;
959 
960 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
961 	if (ret < 0) {
962 		dev_err(isp->dev,
963 			"Failed to register %s: %d\n", vdev->name, ret);
964 		goto err_entity_cleanup;
965 	}
966 
967 	return 0;
968 
969 err_entity_cleanup:
970 	media_entity_cleanup(&vdev->entity);
971 err_queue_release:
972 	vb2_queue_release(vb2_q);
973 err_detroy:
974 	mutex_destroy(&params->lock);
975 	return ret;
976 }
977 
978 void c3_isp_params_unregister(struct c3_isp_device *isp)
979 {
980 	struct c3_isp_params *params = &isp->params;
981 
982 	vb2_queue_release(&params->vb2_q);
983 	media_entity_cleanup(&params->vdev.entity);
984 	video_unregister_device(&params->vdev);
985 	mutex_destroy(&params->lock);
986 }
987 
988 void c3_isp_params_isr(struct c3_isp_device *isp)
989 {
990 	struct c3_isp_params *params = &isp->params;
991 
992 	guard(spinlock_irqsave)(&params->buff_lock);
993 
994 	params->buff =
995 		list_first_entry_or_null(&params->pending,
996 					 struct c3_isp_params_buffer, list);
997 	if (!params->buff)
998 		return;
999 
1000 	list_del(&params->buff->list);
1001 
1002 	c3_isp_params_cfg_blocks(params);
1003 
1004 	params->buff->vb.sequence = params->isp->frm_sequence;
1005 	params->buff->vb.vb2_buf.timestamp = ktime_get();
1006 	params->buff->vb.field = V4L2_FIELD_NONE;
1007 	vb2_buffer_done(&params->buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
1008 }
1009