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 ; |
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, val: 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, val: 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, val: 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, val: 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, cfg: awb_cfg); |
206 | c3_isp_params_awb_cood(isp, cfg: 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, val: 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, val: 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, val: 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, val: 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, cfg: ae_cfg); |
304 | c3_isp_params_ae_cood(isp, cfg: 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, val: 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, cfg: 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)(l: ¶ms->buff_lock); |
670 | |
671 | /* Only use the first buffer to initialize ISP */ |
672 | params->buff = |
673 | list_first_entry_or_null(¶ms->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(q: vb->vb2_queue); |
763 | |
764 | guard(spinlock_irqsave)(l: ¶ms->buff_lock); |
765 | |
766 | list_add_tail(new: &buf->list, head: ¶ms->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(q: vb->vb2_queue); |
774 | struct c3_isp_params_cfg *cfg = buf->cfg; |
775 | struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, plane_no: 0); |
776 | size_t payload_size = vb2_get_plane_payload(vb, plane_no: 0); |
777 | size_t = 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(q: 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(addr: 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)(l: ¶ms->buff_lock); |
895 | |
896 | while (!list_empty(head: ¶ms->pending)) { |
897 | buff = list_first_entry(¶ms->pending, |
898 | struct c3_isp_params_buffer, list); |
899 | list_del(entry: &buff->list); |
900 | vb2_buffer_done(vb: &buff->vb.vb2_buf, state: 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 = ¶ms->vdev; |
917 | struct vb2_queue *vb2_q = ¶ms->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(list: ¶ms->pending); |
925 | spin_lock_init(¶ms->buff_lock); |
926 | mutex_init(¶ms->lock); |
927 | |
928 | snprintf(buf: vdev->name, size: sizeof(vdev->name), fmt: "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 = ¶ms->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, data: 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 = ¶ms->lock; |
949 | vb2_q->min_queued_buffers = 1; |
950 | |
951 | ret = vb2_queue_init(q: vb2_q); |
952 | if (ret) |
953 | goto err_detroy; |
954 | |
955 | params->pad.flags = MEDIA_PAD_FL_SOURCE; |
956 | ret = media_entity_pads_init(entity: &vdev->entity, num_pads: 1, pads: ¶ms->pad); |
957 | if (ret) |
958 | goto err_queue_release; |
959 | |
960 | ret = video_register_device(vdev, type: VFL_TYPE_VIDEO, nr: -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(entity: &vdev->entity); |
971 | err_queue_release: |
972 | vb2_queue_release(q: vb2_q); |
973 | err_detroy: |
974 | mutex_destroy(lock: ¶ms->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(q: ¶ms->vb2_q); |
983 | media_entity_cleanup(entity: ¶ms->vdev.entity); |
984 | video_unregister_device(vdev: ¶ms->vdev); |
985 | mutex_destroy(lock: ¶ms->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)(l: ¶ms->buff_lock); |
993 | |
994 | params->buff = |
995 | list_first_entry_or_null(¶ms->pending, |
996 | struct c3_isp_params_buffer, list); |
997 | if (!params->buff) |
998 | return; |
999 | |
1000 | list_del(entry: ¶ms->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(vb: ¶ms->buff->vb.vb2_buf, state: VB2_BUF_STATE_DONE); |
1008 | } |
1009 | |