Commit | Line | Data |
---|---|---|
a49d2536 AC |
1 | /* |
2 | * Support for Medifield PNW Camera Imaging ISP subsystem. | |
3 | * | |
4 | * Copyright (c) 2010 Intel Corporation. All Rights Reserved. | |
5 | * | |
6 | * Copyright (c) 2010 Silicon Hive www.siliconhive.com. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License version | |
10 | * 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
a49d2536 AC |
17 | * |
18 | */ | |
19 | #include <linux/firmware.h> | |
20 | #include <linux/pci.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/kernel.h> | |
23 | #include <linux/kfifo.h> | |
24 | #include <linux/pm_runtime.h> | |
25 | #include <linux/timer.h> | |
66228be9 | 26 | |
66228be9 | 27 | #include <asm/iosf_mbi.h> |
a49d2536 AC |
28 | |
29 | #include <media/v4l2-event.h> | |
30 | #include <media/videobuf-vmalloc.h> | |
31 | ||
32 | #define CREATE_TRACE_POINTS | |
33 | #include "atomisp_trace_event.h" | |
34 | ||
35 | #include "atomisp_cmd.h" | |
36 | #include "atomisp_common.h" | |
37 | #include "atomisp_fops.h" | |
38 | #include "atomisp_internal.h" | |
39 | #include "atomisp_ioctl.h" | |
40 | #include "atomisp-regs.h" | |
41 | #include "atomisp_tables.h" | |
42 | #include "atomisp_acc.h" | |
43 | #include "atomisp_compat.h" | |
44 | #include "atomisp_subdev.h" | |
45 | #include "atomisp_dfs_tables.h" | |
46 | ||
47 | #include "hrt/hive_isp_css_mm_hrt.h" | |
48 | ||
49 | #include "sh_css_hrt.h" | |
50 | #include "sh_css_defs.h" | |
51 | #include "system_global.h" | |
52 | #include "sh_css_internal.h" | |
53 | #include "sh_css_sp.h" | |
54 | #include "gp_device.h" | |
55 | #include "device_access.h" | |
56 | #include "irq.h" | |
57 | ||
58 | #include "ia_css_types.h" | |
59 | #include "ia_css_stream.h" | |
60 | #include "error_support.h" | |
61 | #include "hrt/bits.h" | |
62 | ||
63 | ||
64 | /* We should never need to run the flash for more than 2 frames. | |
65 | * At 15fps this means 133ms. We set the timeout a bit longer. | |
66 | * Each flash driver is supposed to set its own timeout, but | |
67 | * just in case someone else changed the timeout, we set it | |
68 | * here to make sure we don't damage the flash hardware. */ | |
69 | #define FLASH_TIMEOUT 800 /* ms */ | |
70 | ||
71 | union host { | |
72 | struct { | |
73 | void *kernel_ptr; | |
74 | void __user *user_ptr; | |
75 | int size; | |
76 | } scalar; | |
77 | struct { | |
78 | void *hmm_ptr; | |
79 | } ptr; | |
80 | }; | |
81 | ||
a49d2536 AC |
82 | /* |
83 | * get sensor:dis71430/ov2720 related info from v4l2_subdev->priv data field. | |
84 | * subdev->priv is set in mrst.c | |
85 | */ | |
86 | struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd) | |
87 | { | |
88 | return (struct camera_mipi_info *)v4l2_get_subdev_hostdata(sd); | |
89 | } | |
90 | ||
91 | /* | |
92 | * get struct atomisp_video_pipe from v4l2 video_device | |
93 | */ | |
94 | struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev) | |
95 | { | |
96 | return (struct atomisp_video_pipe *) | |
97 | container_of(dev, struct atomisp_video_pipe, vdev); | |
98 | } | |
99 | ||
100 | /* | |
101 | * get struct atomisp_acc_pipe from v4l2 video_device | |
102 | */ | |
103 | struct atomisp_acc_pipe *atomisp_to_acc_pipe(struct video_device *dev) | |
104 | { | |
105 | return (struct atomisp_acc_pipe *) | |
106 | container_of(dev, struct atomisp_acc_pipe, vdev); | |
107 | } | |
108 | ||
109 | static unsigned short atomisp_get_sensor_fps(struct atomisp_sub_device *asd) | |
110 | { | |
c6299080 | 111 | struct v4l2_subdev_frame_interval fi; |
a49d2536 | 112 | struct atomisp_device *isp = asd->isp; |
c6299080 DY |
113 | |
114 | unsigned short fps = 0; | |
115 | int ret; | |
116 | ||
117 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
118 | video, g_frame_interval, &fi); | |
119 | ||
120 | if (!ret && fi.interval.numerator) | |
121 | fps = fi.interval.denominator / fi.interval.numerator; | |
122 | ||
a49d2536 AC |
123 | return fps; |
124 | } | |
125 | ||
126 | /* | |
127 | * DFS progress is shown as follows: | |
128 | * 1. Target frequency is calculated according to FPS/Resolution/ISP running | |
129 | * mode. | |
130 | * 2. Ratio is calculated using formula: 2 * HPLL / target frequency - 1 | |
131 | * with proper rounding. | |
132 | * 3. Set ratio to ISPFREQ40, 1 to FREQVALID and ISPFREQGUAR40 | |
133 | * to 200MHz in ISPSSPM1. | |
134 | * 4. Wait for FREQVALID to be cleared by P-Unit. | |
135 | * 5. Wait for field ISPFREQSTAT40 in ISPSSPM1 turn to ratio set in 3. | |
136 | */ | |
137 | static int write_target_freq_to_hw(struct atomisp_device *isp, | |
138 | unsigned int new_freq) | |
139 | { | |
140 | unsigned int ratio, timeout, guar_ratio; | |
141 | u32 isp_sspm1 = 0; | |
142 | int i; | |
66228be9 | 143 | |
a49d2536 AC |
144 | if (!isp->hpll_freq) { |
145 | dev_err(isp->dev, "failed to get hpll_freq. no change to freq\n"); | |
146 | return -EINVAL; | |
147 | } | |
148 | ||
66228be9 | 149 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
150 | if (isp_sspm1 & ISP_FREQ_VALID_MASK) { |
151 | dev_dbg(isp->dev, "clearing ISPSSPM1 valid bit.\n"); | |
66228be9 | 152 | iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, ISPSSPM1, |
a49d2536 AC |
153 | isp_sspm1 & ~(1 << ISP_FREQ_VALID_OFFSET)); |
154 | } | |
155 | ||
156 | ratio = (2 * isp->hpll_freq + new_freq / 2) / new_freq - 1; | |
157 | guar_ratio = (2 * isp->hpll_freq + 200 / 2) / 200 - 1; | |
158 | ||
66228be9 | 159 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
160 | isp_sspm1 &= ~(0x1F << ISP_REQ_FREQ_OFFSET); |
161 | ||
162 | for (i = 0; i < ISP_DFS_TRY_TIMES; i++) { | |
66228be9 | 163 | iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, ISPSSPM1, |
a49d2536 AC |
164 | isp_sspm1 |
165 | | ratio << ISP_REQ_FREQ_OFFSET | |
166 | | 1 << ISP_FREQ_VALID_OFFSET | |
167 | | guar_ratio << ISP_REQ_GUAR_FREQ_OFFSET); | |
168 | ||
66228be9 | 169 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
170 | timeout = 20; |
171 | while ((isp_sspm1 & ISP_FREQ_VALID_MASK) && timeout) { | |
66228be9 | 172 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
173 | dev_dbg(isp->dev, "waiting for ISPSSPM1 valid bit to be 0.\n"); |
174 | udelay(100); | |
175 | timeout--; | |
176 | } | |
177 | ||
178 | if (timeout != 0) | |
179 | break; | |
180 | } | |
181 | ||
182 | if (timeout == 0) { | |
183 | dev_err(isp->dev, "DFS failed due to HW error.\n"); | |
184 | return -EINVAL; | |
185 | } | |
186 | ||
66228be9 | 187 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
188 | timeout = 10; |
189 | while (((isp_sspm1 >> ISP_FREQ_STAT_OFFSET) != ratio) && timeout) { | |
66228be9 | 190 | iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM1, &isp_sspm1); |
a49d2536 AC |
191 | dev_dbg(isp->dev, "waiting for ISPSSPM1 status bit to be 0x%x.\n", |
192 | new_freq); | |
193 | udelay(100); | |
194 | timeout--; | |
195 | } | |
196 | if (timeout == 0) { | |
197 | dev_err(isp->dev, "DFS target freq is rejected by HW.\n"); | |
198 | return -EINVAL; | |
199 | } | |
200 | ||
201 | return 0; | |
202 | } | |
203 | int atomisp_freq_scaling(struct atomisp_device *isp, | |
204 | enum atomisp_dfs_mode mode, | |
205 | bool force) | |
206 | { | |
207 | /* FIXME! Only use subdev[0] status yet */ | |
208 | struct atomisp_sub_device *asd = &isp->asd[0]; | |
7469cd3c | 209 | const struct atomisp_dfs_config *dfs; |
a49d2536 AC |
210 | unsigned int new_freq; |
211 | struct atomisp_freq_scaling_rule curr_rules; | |
212 | int i, ret; | |
213 | unsigned short fps = 0; | |
214 | ||
215 | if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP) { | |
216 | dev_err(isp->dev, "DFS cannot proceed due to no power.\n"); | |
217 | return -EINVAL; | |
218 | } | |
219 | ||
a49d2536 AC |
220 | if ((isp->pdev->device & ATOMISP_PCI_DEVICE_SOC_MASK) == |
221 | ATOMISP_PCI_DEVICE_SOC_CHT && ATOMISP_USE_YUVPP(asd)) | |
222 | isp->dfs = &dfs_config_cht_soc; | |
223 | ||
7469cd3c DY |
224 | dfs = isp->dfs; |
225 | ||
226 | if (dfs->lowest_freq == 0 || dfs->max_freq_at_vmin == 0 || | |
227 | dfs->highest_freq == 0 || dfs->dfs_table_size == 0 || | |
228 | !dfs->dfs_table) { | |
a49d2536 AC |
229 | dev_err(isp->dev, "DFS configuration is invalid.\n"); |
230 | return -EINVAL; | |
231 | } | |
232 | ||
233 | if (mode == ATOMISP_DFS_MODE_LOW) { | |
7469cd3c | 234 | new_freq = dfs->lowest_freq; |
a49d2536 AC |
235 | goto done; |
236 | } | |
237 | ||
238 | if (mode == ATOMISP_DFS_MODE_MAX) { | |
7469cd3c | 239 | new_freq = dfs->highest_freq; |
a49d2536 AC |
240 | goto done; |
241 | } | |
242 | ||
243 | fps = atomisp_get_sensor_fps(asd); | |
244 | if (fps == 0) | |
245 | return -EINVAL; | |
246 | ||
247 | curr_rules.width = asd->fmt[asd->capture_pad].fmt.width; | |
248 | curr_rules.height = asd->fmt[asd->capture_pad].fmt.height; | |
249 | curr_rules.fps = fps; | |
250 | curr_rules.run_mode = asd->run_mode->val; | |
251 | /* | |
252 | * For continuous mode, we need to make the capture setting applied | |
253 | * since preview mode, because there is no chance to do this when | |
254 | * starting image capture. | |
255 | */ | |
256 | if (asd->continuous_mode->val) { | |
257 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) | |
258 | curr_rules.run_mode = ATOMISP_RUN_MODE_SDV; | |
259 | else | |
260 | curr_rules.run_mode = | |
261 | ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE; | |
262 | } | |
263 | ||
264 | /* search for the target frequency by looping freq rules*/ | |
7469cd3c DY |
265 | for (i = 0; i < dfs->dfs_table_size; i++) { |
266 | if (curr_rules.width != dfs->dfs_table[i].width && | |
267 | dfs->dfs_table[i].width != ISP_FREQ_RULE_ANY) | |
a49d2536 | 268 | continue; |
7469cd3c DY |
269 | if (curr_rules.height != dfs->dfs_table[i].height && |
270 | dfs->dfs_table[i].height != ISP_FREQ_RULE_ANY) | |
a49d2536 | 271 | continue; |
7469cd3c DY |
272 | if (curr_rules.fps != dfs->dfs_table[i].fps && |
273 | dfs->dfs_table[i].fps != ISP_FREQ_RULE_ANY) | |
a49d2536 | 274 | continue; |
7469cd3c DY |
275 | if (curr_rules.run_mode != dfs->dfs_table[i].run_mode && |
276 | dfs->dfs_table[i].run_mode != ISP_FREQ_RULE_ANY) | |
a49d2536 AC |
277 | continue; |
278 | break; | |
279 | } | |
280 | ||
7469cd3c DY |
281 | if (i == dfs->dfs_table_size) |
282 | new_freq = dfs->max_freq_at_vmin; | |
a49d2536 | 283 | else |
7469cd3c | 284 | new_freq = dfs->dfs_table[i].isp_freq; |
a49d2536 AC |
285 | |
286 | done: | |
287 | dev_dbg(isp->dev, "DFS target frequency=%d.\n", new_freq); | |
288 | ||
289 | if ((new_freq == isp->sw_contex.running_freq) && !force) | |
290 | return 0; | |
291 | ||
292 | dev_dbg(isp->dev, "Programming DFS frequency to %d\n", new_freq); | |
293 | ||
294 | ret = write_target_freq_to_hw(isp, new_freq); | |
295 | if (!ret) { | |
296 | isp->sw_contex.running_freq = new_freq; | |
297 | trace_ipu_pstate(new_freq, -1); | |
298 | } | |
299 | return ret; | |
300 | } | |
301 | ||
302 | /* | |
303 | * reset and restore ISP | |
304 | */ | |
305 | int atomisp_reset(struct atomisp_device *isp) | |
306 | { | |
307 | /* Reset ISP by power-cycling it */ | |
308 | int ret = 0; | |
309 | ||
310 | dev_dbg(isp->dev, "%s\n", __func__); | |
311 | atomisp_css_suspend(isp); | |
312 | ret = atomisp_runtime_suspend(isp->dev); | |
313 | if (ret < 0) | |
314 | dev_err(isp->dev, "atomisp_runtime_suspend failed, %d\n", ret); | |
315 | ret = atomisp_mrfld_power_down(isp); | |
316 | if (ret < 0) { | |
317 | dev_err(isp->dev, "can not disable ISP power\n"); | |
318 | } else { | |
319 | ret = atomisp_mrfld_power_up(isp); | |
320 | if (ret < 0) | |
321 | dev_err(isp->dev, "can not enable ISP power\n"); | |
322 | ret = atomisp_runtime_resume(isp->dev); | |
323 | if (ret < 0) | |
324 | dev_err(isp->dev, "atomisp_runtime_resume failed, %d\n", ret); | |
325 | } | |
326 | ret = atomisp_css_resume(isp); | |
327 | if (ret) | |
328 | isp->isp_fatal_error = true; | |
329 | ||
330 | return ret; | |
331 | } | |
332 | ||
333 | /* | |
287666df | 334 | * interrupt disable functions |
a49d2536 | 335 | */ |
287666df | 336 | static void disable_isp_irq(enum hrt_isp_css_irq irq) |
a49d2536 | 337 | { |
287666df | 338 | irq_disable_channel(IRQ0_ID, irq); |
a49d2536 | 339 | |
287666df DY |
340 | if (irq != hrt_isp_css_irq_sp) |
341 | return; | |
342 | ||
343 | cnd_sp_irq_enable(SP0_ID, false); | |
a49d2536 AC |
344 | } |
345 | ||
346 | /* | |
347 | * interrupt clean function | |
348 | */ | |
349 | static void clear_isp_irq(enum hrt_isp_css_irq irq) | |
350 | { | |
351 | irq_clear_all(IRQ0_ID); | |
352 | } | |
353 | ||
354 | void atomisp_msi_irq_init(struct atomisp_device *isp, struct pci_dev *dev) | |
355 | { | |
356 | u32 msg32; | |
357 | u16 msg16; | |
358 | ||
359 | pci_read_config_dword(dev, PCI_MSI_CAPID, &msg32); | |
360 | msg32 |= 1 << MSI_ENABLE_BIT; | |
361 | pci_write_config_dword(dev, PCI_MSI_CAPID, msg32); | |
362 | ||
363 | msg32 = (1 << INTR_IER) | (1 << INTR_IIR); | |
364 | pci_write_config_dword(dev, PCI_INTERRUPT_CTRL, msg32); | |
365 | ||
366 | pci_read_config_word(dev, PCI_COMMAND, &msg16); | |
367 | msg16 |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | | |
368 | PCI_COMMAND_INTX_DISABLE); | |
369 | pci_write_config_word(dev, PCI_COMMAND, msg16); | |
370 | } | |
371 | ||
372 | void atomisp_msi_irq_uninit(struct atomisp_device *isp, struct pci_dev *dev) | |
373 | { | |
374 | u32 msg32; | |
375 | u16 msg16; | |
376 | ||
377 | pci_read_config_dword(dev, PCI_MSI_CAPID, &msg32); | |
378 | msg32 &= ~(1 << MSI_ENABLE_BIT); | |
379 | pci_write_config_dword(dev, PCI_MSI_CAPID, msg32); | |
380 | ||
381 | msg32 = 0x0; | |
382 | pci_write_config_dword(dev, PCI_INTERRUPT_CTRL, msg32); | |
383 | ||
384 | pci_read_config_word(dev, PCI_COMMAND, &msg16); | |
385 | msg16 &= ~(PCI_COMMAND_MASTER); | |
386 | pci_write_config_word(dev, PCI_COMMAND, msg16); | |
387 | } | |
388 | ||
389 | static void atomisp_sof_event(struct atomisp_sub_device *asd) | |
390 | { | |
391 | struct v4l2_event event = {0}; | |
392 | ||
393 | event.type = V4L2_EVENT_FRAME_SYNC; | |
394 | event.u.frame_sync.frame_sequence = atomic_read(&asd->sof_count); | |
395 | ||
396 | v4l2_event_queue(asd->subdev.devnode, &event); | |
397 | } | |
398 | ||
399 | void atomisp_eof_event(struct atomisp_sub_device *asd, uint8_t exp_id) | |
400 | { | |
401 | struct v4l2_event event = {0}; | |
402 | ||
403 | event.type = V4L2_EVENT_FRAME_END; | |
404 | event.u.frame_sync.frame_sequence = exp_id; | |
405 | ||
406 | v4l2_event_queue(asd->subdev.devnode, &event); | |
407 | } | |
408 | ||
409 | static void atomisp_3a_stats_ready_event(struct atomisp_sub_device *asd, uint8_t exp_id) | |
410 | { | |
411 | struct v4l2_event event = {0}; | |
412 | ||
413 | event.type = V4L2_EVENT_ATOMISP_3A_STATS_READY; | |
414 | event.u.frame_sync.frame_sequence = exp_id; | |
415 | ||
416 | v4l2_event_queue(asd->subdev.devnode, &event); | |
417 | } | |
418 | ||
419 | static void atomisp_metadata_ready_event(struct atomisp_sub_device *asd, | |
4995706d | 420 | enum atomisp_metadata_type md_type) |
a49d2536 AC |
421 | { |
422 | struct v4l2_event event = {0}; | |
423 | ||
424 | event.type = V4L2_EVENT_ATOMISP_METADATA_READY; | |
425 | event.u.data[0] = md_type; | |
426 | ||
427 | v4l2_event_queue(asd->subdev.devnode, &event); | |
428 | } | |
429 | ||
430 | static void atomisp_reset_event(struct atomisp_sub_device *asd) | |
431 | { | |
432 | struct v4l2_event event = {0}; | |
433 | ||
434 | event.type = V4L2_EVENT_ATOMISP_CSS_RESET; | |
435 | ||
436 | v4l2_event_queue(asd->subdev.devnode, &event); | |
437 | } | |
438 | ||
439 | ||
440 | static void print_csi_rx_errors(enum ia_css_csi2_port port, | |
441 | struct atomisp_device *isp) | |
442 | { | |
443 | u32 infos = 0; | |
444 | ||
445 | atomisp_css_rx_get_irq_info(port, &infos); | |
446 | ||
447 | dev_err(isp->dev, "CSI Receiver port %d errors:\n", port); | |
448 | if (infos & CSS_RX_IRQ_INFO_BUFFER_OVERRUN) | |
449 | dev_err(isp->dev, " buffer overrun"); | |
450 | if (infos & CSS_RX_IRQ_INFO_ERR_SOT) | |
451 | dev_err(isp->dev, " start-of-transmission error"); | |
452 | if (infos & CSS_RX_IRQ_INFO_ERR_SOT_SYNC) | |
453 | dev_err(isp->dev, " start-of-transmission sync error"); | |
454 | if (infos & CSS_RX_IRQ_INFO_ERR_CONTROL) | |
455 | dev_err(isp->dev, " control error"); | |
456 | if (infos & CSS_RX_IRQ_INFO_ERR_ECC_DOUBLE) | |
457 | dev_err(isp->dev, " 2 or more ECC errors"); | |
458 | if (infos & CSS_RX_IRQ_INFO_ERR_CRC) | |
459 | dev_err(isp->dev, " CRC mismatch"); | |
460 | if (infos & CSS_RX_IRQ_INFO_ERR_UNKNOWN_ID) | |
461 | dev_err(isp->dev, " unknown error"); | |
462 | if (infos & CSS_RX_IRQ_INFO_ERR_FRAME_SYNC) | |
463 | dev_err(isp->dev, " frame sync error"); | |
464 | if (infos & CSS_RX_IRQ_INFO_ERR_FRAME_DATA) | |
465 | dev_err(isp->dev, " frame data error"); | |
466 | if (infos & CSS_RX_IRQ_INFO_ERR_DATA_TIMEOUT) | |
467 | dev_err(isp->dev, " data timeout"); | |
468 | if (infos & CSS_RX_IRQ_INFO_ERR_UNKNOWN_ESC) | |
469 | dev_err(isp->dev, " unknown escape command entry"); | |
470 | if (infos & CSS_RX_IRQ_INFO_ERR_LINE_SYNC) | |
471 | dev_err(isp->dev, " line sync error"); | |
472 | } | |
473 | ||
474 | /* Clear irq reg */ | |
475 | static void clear_irq_reg(struct atomisp_device *isp) | |
476 | { | |
477 | u32 msg_ret; | |
478 | pci_read_config_dword(isp->pdev, PCI_INTERRUPT_CTRL, &msg_ret); | |
479 | msg_ret |= 1 << INTR_IIR; | |
480 | pci_write_config_dword(isp->pdev, PCI_INTERRUPT_CTRL, msg_ret); | |
481 | } | |
482 | ||
483 | static struct atomisp_sub_device * | |
484 | __get_asd_from_port(struct atomisp_device *isp, mipi_port_ID_t port) | |
485 | { | |
486 | int i; | |
487 | ||
488 | /* Check which isp subdev to send eof */ | |
489 | for (i = 0; i < isp->num_of_streams; i++) { | |
490 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
5eb32874 DY |
491 | struct camera_mipi_info *mipi_info; |
492 | ||
493 | mipi_info = atomisp_to_sensor_mipi_info( | |
494 | isp->inputs[asd->input_curr].camera); | |
495 | ||
7eb21a88 | 496 | if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED && |
a49d2536 | 497 | __get_mipi_port(isp, mipi_info->port) == port) { |
7eb21a88 | 498 | return asd; |
a49d2536 AC |
499 | } |
500 | } | |
501 | ||
502 | return NULL; | |
503 | } | |
504 | ||
505 | /* interrupt handling function*/ | |
506 | irqreturn_t atomisp_isr(int irq, void *dev) | |
507 | { | |
508 | struct atomisp_device *isp = (struct atomisp_device *)dev; | |
509 | struct atomisp_sub_device *asd; | |
510 | struct atomisp_css_event eof_event; | |
511 | unsigned int irq_infos = 0; | |
512 | unsigned long flags; | |
513 | unsigned int i; | |
514 | int err; | |
515 | ||
516 | spin_lock_irqsave(&isp->lock, flags); | |
517 | if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP || | |
518 | isp->css_initialized == false) { | |
519 | spin_unlock_irqrestore(&isp->lock, flags); | |
520 | return IRQ_HANDLED; | |
521 | } | |
522 | err = atomisp_css_irq_translate(isp, &irq_infos); | |
523 | if (err) { | |
524 | spin_unlock_irqrestore(&isp->lock, flags); | |
525 | return IRQ_NONE; | |
526 | } | |
527 | ||
528 | dev_dbg(isp->dev, "irq:0x%x\n", irq_infos); | |
529 | ||
530 | clear_irq_reg(isp); | |
531 | ||
532 | if (!atomisp_streaming_count(isp) && !atomisp_is_acc_enabled(isp)) | |
533 | goto out_nowake; | |
534 | ||
535 | for (i = 0; i < isp->num_of_streams; i++) { | |
536 | asd = &isp->asd[i]; | |
537 | ||
538 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
539 | continue; | |
540 | /* | |
541 | * Current SOF only support one stream, so the SOF only valid | |
542 | * either solely one stream is running | |
543 | */ | |
544 | if (irq_infos & CSS_IRQ_INFO_CSS_RECEIVER_SOF) { | |
545 | atomic_inc(&asd->sof_count); | |
546 | atomisp_sof_event(asd); | |
547 | ||
548 | /* If sequence_temp and sequence are the same | |
549 | * there where no frames lost so we can increase | |
550 | * sequence_temp. | |
551 | * If not then processing of frame is still in progress | |
552 | * and driver needs to keep old sequence_temp value. | |
553 | * NOTE: There is assumption here that ISP will not | |
554 | * start processing next frame from sensor before old | |
555 | * one is completely done. */ | |
556 | if (atomic_read(&asd->sequence) == atomic_read( | |
557 | &asd->sequence_temp)) | |
558 | atomic_set(&asd->sequence_temp, | |
559 | atomic_read(&asd->sof_count)); | |
560 | } | |
561 | if (irq_infos & CSS_IRQ_INFO_EVENTS_READY) | |
562 | atomic_set(&asd->sequence, | |
4995706d | 563 | atomic_read(&asd->sequence_temp)); |
a49d2536 AC |
564 | } |
565 | ||
566 | if (irq_infos & CSS_IRQ_INFO_CSS_RECEIVER_SOF) | |
567 | irq_infos &= ~CSS_IRQ_INFO_CSS_RECEIVER_SOF; | |
568 | ||
569 | if ((irq_infos & CSS_IRQ_INFO_INPUT_SYSTEM_ERROR) || | |
4995706d | 570 | (irq_infos & CSS_IRQ_INFO_IF_ERROR)) { |
a49d2536 AC |
571 | /* handle mipi receiver error */ |
572 | u32 rx_infos; | |
573 | enum ia_css_csi2_port port; | |
574 | ||
575 | for (port = IA_CSS_CSI2_PORT0; port <= IA_CSS_CSI2_PORT2; | |
576 | port++) { | |
577 | print_csi_rx_errors(port, isp); | |
578 | atomisp_css_rx_get_irq_info(port, &rx_infos); | |
579 | atomisp_css_rx_clear_irq_info(port, rx_infos); | |
580 | } | |
581 | } | |
582 | ||
583 | if (irq_infos & IA_CSS_IRQ_INFO_ISYS_EVENTS_READY) { | |
584 | while (ia_css_dequeue_isys_event(&(eof_event.event)) == | |
585 | IA_CSS_SUCCESS) { | |
586 | /* EOF Event does not have the css_pipe returned */ | |
587 | asd = __get_asd_from_port(isp, eof_event.event.port); | |
588 | if (!asd) { | |
589 | dev_err(isp->dev, "%s:no subdev.event:%d", __func__, | |
590 | eof_event.event.type); | |
591 | continue; | |
592 | } | |
593 | ||
594 | atomisp_eof_event(asd, eof_event.event.exp_id); | |
a49d2536 AC |
595 | dev_dbg(isp->dev, "%s EOF exp_id %d, asd %d\n", |
596 | __func__, eof_event.event.exp_id, asd->index); | |
a49d2536 AC |
597 | } |
598 | ||
599 | irq_infos &= ~IA_CSS_IRQ_INFO_ISYS_EVENTS_READY; | |
600 | if (irq_infos == 0) | |
601 | goto out_nowake; | |
602 | } | |
603 | ||
604 | spin_unlock_irqrestore(&isp->lock, flags); | |
605 | ||
606 | return IRQ_WAKE_THREAD; | |
607 | ||
608 | out_nowake: | |
609 | spin_unlock_irqrestore(&isp->lock, flags); | |
610 | ||
611 | return IRQ_HANDLED; | |
612 | } | |
613 | ||
614 | void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd) | |
615 | { | |
616 | int i; | |
617 | memset(asd->s3a_bufs_in_css, 0, sizeof(asd->s3a_bufs_in_css)); | |
618 | for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) | |
619 | memset(asd->metadata_bufs_in_css[i], 0, | |
4995706d | 620 | sizeof(asd->metadata_bufs_in_css[i])); |
a49d2536 AC |
621 | asd->dis_bufs_in_css = 0; |
622 | asd->video_out_capture.buffers_in_css = 0; | |
623 | asd->video_out_vf.buffers_in_css = 0; | |
624 | asd->video_out_preview.buffers_in_css = 0; | |
625 | asd->video_out_video_capture.buffers_in_css = 0; | |
626 | } | |
627 | ||
628 | #ifndef ISP2401 | |
629 | bool atomisp_buffers_queued(struct atomisp_sub_device *asd) | |
630 | #else | |
631 | bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe) | |
632 | #endif | |
633 | { | |
634 | #ifndef ISP2401 | |
635 | return asd->video_out_capture.buffers_in_css || | |
636 | asd->video_out_vf.buffers_in_css || | |
637 | asd->video_out_preview.buffers_in_css || | |
638 | asd->video_out_video_capture.buffers_in_css ? | |
639 | true : false; | |
640 | #else | |
641 | return pipe->buffers_in_css ? true : false; | |
642 | #endif | |
643 | } | |
644 | ||
645 | /* 0x100000 is the start of dmem inside SP */ | |
646 | #define SP_DMEM_BASE 0x100000 | |
647 | ||
648 | void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr, | |
649 | unsigned int size) | |
650 | { | |
651 | unsigned int data = 0; | |
652 | unsigned int size32 = DIV_ROUND_UP(size, sizeof(u32)); | |
653 | ||
654 | dev_dbg(isp->dev, "atomisp_io_base:%p\n", atomisp_io_base); | |
655 | dev_dbg(isp->dev, "%s, addr:0x%x, size: %d, size32: %d\n", __func__, | |
656 | addr, size, size32); | |
657 | if (size32 * 4 + addr > 0x4000) { | |
658 | dev_err(isp->dev, "illegal size (%d) or addr (0x%x)\n", | |
659 | size32, addr); | |
660 | return; | |
661 | } | |
662 | addr += SP_DMEM_BASE; | |
663 | do { | |
664 | data = _hrt_master_port_uload_32(addr); | |
665 | ||
666 | dev_dbg(isp->dev, "%s, \t [0x%x]:0x%x\n", __func__, addr, data); | |
667 | addr += sizeof(unsigned int); | |
668 | size32 -= 1; | |
669 | } while (size32 > 0); | |
670 | } | |
671 | ||
672 | static struct videobuf_buffer *atomisp_css_frame_to_vbuf( | |
673 | struct atomisp_video_pipe *pipe, struct atomisp_css_frame *frame) | |
674 | { | |
675 | struct videobuf_vmalloc_memory *vm_mem; | |
676 | struct atomisp_css_frame *handle; | |
677 | int i; | |
678 | ||
679 | for (i = 0; pipe->capq.bufs[i]; i++) { | |
680 | vm_mem = pipe->capq.bufs[i]->priv; | |
681 | handle = vm_mem->vaddr; | |
682 | if (handle && handle->data == frame->data) | |
683 | return pipe->capq.bufs[i]; | |
684 | } | |
685 | ||
686 | return NULL; | |
687 | } | |
688 | ||
689 | static void get_buf_timestamp(struct timeval *tv) | |
690 | { | |
691 | struct timespec ts; | |
692 | ktime_get_ts(&ts); | |
693 | tv->tv_sec = ts.tv_sec; | |
694 | tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; | |
695 | } | |
696 | ||
697 | static void atomisp_flush_video_pipe(struct atomisp_sub_device *asd, | |
698 | struct atomisp_video_pipe *pipe) | |
699 | { | |
700 | unsigned long irqflags; | |
701 | int i; | |
702 | ||
703 | if (!pipe->users) | |
704 | return; | |
705 | ||
706 | for (i = 0; pipe->capq.bufs[i]; i++) { | |
707 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
708 | if (pipe->capq.bufs[i]->state == VIDEOBUF_ACTIVE || | |
709 | pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) { | |
710 | get_buf_timestamp(&pipe->capq.bufs[i]->ts); | |
711 | pipe->capq.bufs[i]->field_count = | |
712 | atomic_read(&asd->sequence) << 1; | |
713 | dev_dbg(asd->isp->dev, "release buffers on device %s\n", | |
714 | pipe->vdev.name); | |
715 | if (pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) | |
716 | list_del_init(&pipe->capq.bufs[i]->queue); | |
717 | pipe->capq.bufs[i]->state = VIDEOBUF_ERROR; | |
718 | wake_up(&pipe->capq.bufs[i]->done); | |
719 | } | |
720 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
721 | } | |
722 | } | |
723 | ||
724 | /* Returns queued buffers back to video-core */ | |
725 | void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd) | |
726 | { | |
727 | atomisp_flush_video_pipe(asd, &asd->video_out_capture); | |
728 | atomisp_flush_video_pipe(asd, &asd->video_out_vf); | |
729 | atomisp_flush_video_pipe(asd, &asd->video_out_preview); | |
730 | atomisp_flush_video_pipe(asd, &asd->video_out_video_capture); | |
731 | } | |
732 | ||
733 | /* clean out the parameters that did not apply */ | |
734 | void atomisp_flush_params_queue(struct atomisp_video_pipe *pipe) | |
735 | { | |
736 | struct atomisp_css_params_with_list *param; | |
737 | ||
738 | while (!list_empty(&pipe->per_frame_params)) { | |
739 | param = list_entry(pipe->per_frame_params.next, | |
4995706d | 740 | struct atomisp_css_params_with_list, list); |
a49d2536 AC |
741 | list_del(¶m->list); |
742 | atomisp_free_css_parameters(¶m->params); | |
05f1d92f | 743 | kvfree(param); |
a49d2536 AC |
744 | } |
745 | } | |
746 | ||
747 | /* Re-queue per-frame parameters */ | |
748 | static void atomisp_recover_params_queue(struct atomisp_video_pipe *pipe) | |
749 | { | |
750 | struct atomisp_css_params_with_list *param; | |
751 | int i; | |
752 | ||
753 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
754 | param = pipe->frame_params[i]; | |
755 | if (param) | |
756 | list_add_tail(¶m->list, &pipe->per_frame_params); | |
757 | pipe->frame_params[i] = NULL; | |
758 | } | |
759 | atomisp_handle_parameter_and_buffer(pipe); | |
760 | } | |
761 | ||
762 | /* find atomisp_video_pipe with css pipe id, buffer type and atomisp run_mode */ | |
763 | static struct atomisp_video_pipe *__atomisp_get_pipe( | |
764 | struct atomisp_sub_device *asd, | |
765 | enum atomisp_input_stream_id stream_id, | |
766 | enum atomisp_css_pipe_id css_pipe_id, | |
767 | enum atomisp_css_buffer_type buf_type) | |
768 | { | |
769 | struct atomisp_device *isp = asd->isp; | |
770 | ||
771 | if (css_pipe_id == CSS_PIPE_ID_COPY && | |
772 | isp->inputs[asd->input_curr].camera_caps-> | |
773 | sensor[asd->sensor_curr].stream_num > 1) { | |
774 | switch (stream_id) { | |
775 | case ATOMISP_INPUT_STREAM_PREVIEW: | |
776 | return &asd->video_out_preview; | |
777 | case ATOMISP_INPUT_STREAM_POSTVIEW: | |
778 | return &asd->video_out_vf; | |
779 | case ATOMISP_INPUT_STREAM_VIDEO: | |
780 | return &asd->video_out_video_capture; | |
781 | case ATOMISP_INPUT_STREAM_CAPTURE: | |
782 | default: | |
783 | return &asd->video_out_capture; | |
784 | } | |
785 | } | |
786 | ||
787 | /* video is same in online as in continuouscapture mode */ | |
788 | if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { | |
789 | /* | |
790 | * Disable vf_pp and run CSS in still capture mode. In this | |
791 | * mode, CSS does not cause extra latency with buffering, but | |
792 | * scaling is not available. | |
793 | */ | |
794 | return &asd->video_out_capture; | |
795 | } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { | |
796 | /* | |
797 | * Disable vf_pp and run CSS in video mode. This allows using | |
798 | * ISP scaling but it has one frame delay due to CSS internal | |
799 | * buffering. | |
800 | */ | |
801 | return &asd->video_out_video_capture; | |
802 | } else if (css_pipe_id == CSS_PIPE_ID_YUVPP) { | |
803 | /* | |
804 | * to SOC camera, yuvpp pipe is run for capture/video/SDV/ZSL. | |
805 | */ | |
806 | if (asd->continuous_mode->val) { | |
807 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
808 | /* SDV case */ | |
809 | switch (buf_type) { | |
810 | case CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: | |
811 | return &asd->video_out_video_capture; | |
812 | case CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: | |
813 | return &asd->video_out_preview; | |
814 | case CSS_BUFFER_TYPE_OUTPUT_FRAME: | |
815 | return &asd->video_out_capture; | |
816 | default: | |
817 | return &asd->video_out_vf; | |
818 | } | |
819 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { | |
820 | /* ZSL case */ | |
821 | switch (buf_type) { | |
822 | case CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: | |
823 | return &asd->video_out_preview; | |
824 | case CSS_BUFFER_TYPE_OUTPUT_FRAME: | |
825 | return &asd->video_out_capture; | |
826 | default: | |
827 | return &asd->video_out_vf; | |
828 | } | |
829 | } | |
830 | } else if (buf_type == CSS_BUFFER_TYPE_OUTPUT_FRAME) { | |
831 | switch (asd->run_mode->val) { | |
832 | case ATOMISP_RUN_MODE_VIDEO: | |
833 | return &asd->video_out_video_capture; | |
834 | case ATOMISP_RUN_MODE_PREVIEW: | |
835 | return &asd->video_out_preview; | |
836 | default: | |
837 | return &asd->video_out_capture; | |
838 | } | |
839 | } else if (buf_type == CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) { | |
840 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) | |
841 | return &asd->video_out_preview; | |
842 | else | |
843 | return &asd->video_out_vf; | |
844 | } | |
845 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
846 | /* For online video or SDV video pipe. */ | |
847 | if (css_pipe_id == CSS_PIPE_ID_VIDEO || | |
435ec8af | 848 | css_pipe_id == CSS_PIPE_ID_COPY) { |
a49d2536 AC |
849 | if (buf_type == CSS_BUFFER_TYPE_OUTPUT_FRAME) |
850 | return &asd->video_out_video_capture; | |
851 | return &asd->video_out_preview; | |
852 | } | |
853 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { | |
854 | /* For online preview or ZSL preview pipe. */ | |
855 | if (css_pipe_id == CSS_PIPE_ID_PREVIEW || | |
435ec8af | 856 | css_pipe_id == CSS_PIPE_ID_COPY) |
a49d2536 AC |
857 | return &asd->video_out_preview; |
858 | } | |
859 | /* For capture pipe. */ | |
860 | if (buf_type == CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) | |
861 | return &asd->video_out_vf; | |
862 | return &asd->video_out_capture; | |
863 | } | |
864 | ||
865 | enum atomisp_metadata_type | |
866 | atomisp_get_metadata_type(struct atomisp_sub_device *asd, | |
867 | enum ia_css_pipe_id pipe_id) | |
868 | { | |
869 | if (!asd->continuous_mode->val) | |
870 | return ATOMISP_MAIN_METADATA; | |
871 | ||
872 | if (pipe_id == IA_CSS_PIPE_ID_CAPTURE) /* online capture pipe */ | |
873 | return ATOMISP_SEC_METADATA; | |
874 | else | |
875 | return ATOMISP_MAIN_METADATA; | |
876 | } | |
877 | ||
878 | void atomisp_buf_done(struct atomisp_sub_device *asd, int error, | |
879 | enum atomisp_css_buffer_type buf_type, | |
880 | enum atomisp_css_pipe_id css_pipe_id, | |
881 | bool q_buffers, enum atomisp_input_stream_id stream_id) | |
882 | { | |
883 | struct videobuf_buffer *vb = NULL; | |
884 | struct atomisp_video_pipe *pipe = NULL; | |
885 | struct atomisp_css_buffer buffer; | |
886 | bool requeue = false; | |
887 | int err; | |
888 | unsigned long irqflags; | |
889 | struct atomisp_css_frame *frame = NULL; | |
890 | struct atomisp_s3a_buf *s3a_buf = NULL, *_s3a_buf_tmp; | |
891 | struct atomisp_dis_buf *dis_buf = NULL, *_dis_buf_tmp; | |
892 | struct atomisp_metadata_buf *md_buf = NULL, *_md_buf_tmp; | |
893 | enum atomisp_metadata_type md_type; | |
894 | struct atomisp_device *isp = asd->isp; | |
895 | struct v4l2_control ctrl; | |
896 | #ifdef ISP2401 | |
897 | bool reset_wdt_timer = false; | |
898 | #endif | |
899 | ||
900 | if ( | |
901 | buf_type != CSS_BUFFER_TYPE_METADATA && | |
902 | buf_type != CSS_BUFFER_TYPE_3A_STATISTICS && | |
903 | buf_type != CSS_BUFFER_TYPE_DIS_STATISTICS && | |
904 | buf_type != CSS_BUFFER_TYPE_OUTPUT_FRAME && | |
905 | buf_type != CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME && | |
906 | buf_type != CSS_BUFFER_TYPE_RAW_OUTPUT_FRAME && | |
907 | buf_type != CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME && | |
908 | buf_type != CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) { | |
909 | dev_err(isp->dev, "%s, unsupported buffer type: %d\n", | |
910 | __func__, buf_type); | |
911 | return; | |
912 | } | |
913 | ||
914 | memset(&buffer, 0, sizeof(struct atomisp_css_buffer)); | |
915 | buffer.css_buffer.type = buf_type; | |
916 | err = atomisp_css_dequeue_buffer(asd, stream_id, css_pipe_id, | |
4995706d | 917 | buf_type, &buffer); |
a49d2536 AC |
918 | if (err) { |
919 | dev_err(isp->dev, | |
920 | "atomisp_css_dequeue_buffer failed: 0x%x\n", err); | |
921 | return; | |
922 | } | |
923 | ||
924 | /* need to know the atomisp pipe for frame buffers */ | |
925 | pipe = __atomisp_get_pipe(asd, stream_id, css_pipe_id, buf_type); | |
926 | if (pipe == NULL) { | |
927 | dev_err(isp->dev, "error getting atomisp pipe\n"); | |
928 | return; | |
929 | } | |
930 | ||
931 | switch (buf_type) { | |
932 | case CSS_BUFFER_TYPE_3A_STATISTICS: | |
933 | list_for_each_entry_safe(s3a_buf, _s3a_buf_tmp, | |
4995706d | 934 | &asd->s3a_stats_in_css, list) { |
a49d2536 AC |
935 | if (s3a_buf->s3a_data == |
936 | buffer.css_buffer.data.stats_3a) { | |
937 | list_del_init(&s3a_buf->list); | |
938 | list_add_tail(&s3a_buf->list, | |
4995706d | 939 | &asd->s3a_stats_ready); |
a49d2536 AC |
940 | break; |
941 | } | |
942 | } | |
943 | ||
944 | asd->s3a_bufs_in_css[css_pipe_id]--; | |
945 | atomisp_3a_stats_ready_event(asd, buffer.css_buffer.exp_id); | |
946 | dev_dbg(isp->dev, "%s: s3a stat with exp_id %d is ready\n", | |
947 | __func__, s3a_buf->s3a_data->exp_id); | |
948 | break; | |
949 | case CSS_BUFFER_TYPE_METADATA: | |
950 | if (error) | |
951 | break; | |
952 | ||
953 | md_type = atomisp_get_metadata_type(asd, css_pipe_id); | |
954 | list_for_each_entry_safe(md_buf, _md_buf_tmp, | |
4995706d | 955 | &asd->metadata_in_css[md_type], list) { |
a49d2536 AC |
956 | if (md_buf->metadata == |
957 | buffer.css_buffer.data.metadata) { | |
958 | list_del_init(&md_buf->list); | |
959 | list_add_tail(&md_buf->list, | |
4995706d | 960 | &asd->metadata_ready[md_type]); |
a49d2536 AC |
961 | break; |
962 | } | |
963 | } | |
964 | asd->metadata_bufs_in_css[stream_id][css_pipe_id]--; | |
965 | atomisp_metadata_ready_event(asd, md_type); | |
966 | dev_dbg(isp->dev, "%s: metadata with exp_id %d is ready\n", | |
967 | __func__, md_buf->metadata->exp_id); | |
968 | break; | |
969 | case CSS_BUFFER_TYPE_DIS_STATISTICS: | |
970 | list_for_each_entry_safe(dis_buf, _dis_buf_tmp, | |
971 | &asd->dis_stats_in_css, list) { | |
972 | if (dis_buf->dis_data == | |
973 | buffer.css_buffer.data.stats_dvs) { | |
974 | spin_lock_irqsave(&asd->dis_stats_lock, | |
4995706d | 975 | irqflags); |
a49d2536 AC |
976 | list_del_init(&dis_buf->list); |
977 | list_add(&dis_buf->list, &asd->dis_stats); | |
978 | asd->params.dis_proj_data_valid = true; | |
979 | spin_unlock_irqrestore(&asd->dis_stats_lock, | |
4995706d | 980 | irqflags); |
a49d2536 AC |
981 | break; |
982 | } | |
983 | } | |
984 | asd->dis_bufs_in_css--; | |
985 | dev_dbg(isp->dev, "%s: dis stat with exp_id %d is ready\n", | |
986 | __func__, dis_buf->dis_data->exp_id); | |
987 | break; | |
988 | case CSS_BUFFER_TYPE_VF_OUTPUT_FRAME: | |
989 | case CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: | |
990 | #ifdef ISP2401 | |
991 | reset_wdt_timer = true; | |
992 | #endif | |
993 | pipe->buffers_in_css--; | |
994 | frame = buffer.css_buffer.data.frame; | |
995 | if (!frame) { | |
996 | WARN_ON(1); | |
997 | break; | |
998 | } | |
999 | if (!frame->valid) | |
1000 | error = true; | |
1001 | ||
1002 | /* FIXME: | |
1003 | * YUVPP doesn't set postview exp_id correctlly in SDV mode. | |
1004 | * This is a WORKAROUND to set exp_id. see HSDES-1503911606. | |
1005 | */ | |
1006 | if (IS_BYT && buf_type == CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME && | |
4995706d | 1007 | asd->continuous_mode->val && ATOMISP_USE_YUVPP(asd)) |
a49d2536 AC |
1008 | frame->exp_id = (asd->postview_exp_id++) % |
1009 | (ATOMISP_MAX_EXP_ID + 1); | |
1010 | ||
1011 | dev_dbg(isp->dev, "%s: vf frame with exp_id %d is ready\n", | |
1012 | __func__, frame->exp_id); | |
1013 | if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) { | |
1014 | if (frame->flash_state | |
1015 | == CSS_FRAME_FLASH_STATE_PARTIAL) | |
1016 | dev_dbg(isp->dev, "%s thumb partially flashed\n", | |
1017 | __func__); | |
1018 | else if (frame->flash_state | |
1019 | == CSS_FRAME_FLASH_STATE_FULL) | |
1020 | dev_dbg(isp->dev, "%s thumb completely flashed\n", | |
1021 | __func__); | |
1022 | else | |
1023 | dev_dbg(isp->dev, "%s thumb no flash in this frame\n", | |
1024 | __func__); | |
1025 | } | |
1026 | vb = atomisp_css_frame_to_vbuf(pipe, frame); | |
1027 | WARN_ON(!vb); | |
1028 | if (vb) | |
1029 | pipe->frame_config_id[vb->i] = frame->isp_config_id; | |
a49d2536 AC |
1030 | if (css_pipe_id == IA_CSS_PIPE_ID_CAPTURE && |
1031 | asd->pending_capture_request > 0) { | |
1032 | err = atomisp_css_offline_capture_configure(asd, | |
a49d2536 AC |
1033 | asd->params.offline_parm.num_captures, |
1034 | asd->params.offline_parm.skip_frames, | |
1035 | asd->params.offline_parm.offset); | |
1036 | #ifndef ISP2401 | |
1037 | asd->pending_capture_request--; | |
1038 | dev_dbg(isp->dev, "Trigger capture again for new buffer. err=%d\n", | |
1039 | err); | |
1040 | #else | |
1041 | asd->pending_capture_request--; | |
1042 | asd->re_trigger_capture = false; | |
1043 | dev_dbg(isp->dev, "Trigger capture again for new buffer. err=%d\n", | |
1044 | err); | |
1045 | } else { | |
1046 | asd->re_trigger_capture = true; | |
1047 | } | |
1048 | #endif | |
1049 | } | |
1050 | break; | |
1051 | case CSS_BUFFER_TYPE_OUTPUT_FRAME: | |
1052 | case CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: | |
1053 | #ifdef ISP2401 | |
1054 | reset_wdt_timer = true; | |
1055 | #endif | |
1056 | pipe->buffers_in_css--; | |
1057 | frame = buffer.css_buffer.data.frame; | |
1058 | if (!frame) { | |
1059 | WARN_ON(1); | |
1060 | break; | |
1061 | } | |
1062 | ||
1063 | if (!frame->valid) | |
1064 | error = true; | |
1065 | ||
1066 | /* FIXME: | |
1067 | * YUVPP doesn't set preview exp_id correctlly in ZSL mode. | |
1068 | * This is a WORKAROUND to set exp_id. see HSDES-1503911606. | |
1069 | */ | |
1070 | if (IS_BYT && buf_type == CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME && | |
4995706d | 1071 | asd->continuous_mode->val && ATOMISP_USE_YUVPP(asd)) |
a49d2536 AC |
1072 | frame->exp_id = (asd->preview_exp_id++) % |
1073 | (ATOMISP_MAX_EXP_ID + 1); | |
1074 | ||
1075 | dev_dbg(isp->dev, "%s: main frame with exp_id %d is ready\n", | |
1076 | __func__, frame->exp_id); | |
1077 | vb = atomisp_css_frame_to_vbuf(pipe, frame); | |
1078 | if (!vb) { | |
1079 | WARN_ON(1); | |
1080 | break; | |
1081 | } | |
1082 | ||
1083 | /* free the parameters */ | |
1084 | if (pipe->frame_params[vb->i]) { | |
1085 | if (asd->params.dvs_6axis == | |
1086 | pipe->frame_params[vb->i]->params.dvs_6axis) | |
1087 | asd->params.dvs_6axis = NULL; | |
1088 | atomisp_free_css_parameters( | |
1089 | &pipe->frame_params[vb->i]->params); | |
05f1d92f | 1090 | kvfree(pipe->frame_params[vb->i]); |
a49d2536 AC |
1091 | pipe->frame_params[vb->i] = NULL; |
1092 | } | |
1093 | ||
1094 | pipe->frame_config_id[vb->i] = frame->isp_config_id; | |
1095 | ctrl.id = V4L2_CID_FLASH_MODE; | |
1096 | if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) { | |
1097 | if (frame->flash_state | |
1098 | == CSS_FRAME_FLASH_STATE_PARTIAL) { | |
1099 | asd->frame_status[vb->i] = | |
1100 | ATOMISP_FRAME_STATUS_FLASH_PARTIAL; | |
1101 | dev_dbg(isp->dev, "%s partially flashed\n", | |
1102 | __func__); | |
1103 | } else if (frame->flash_state | |
1104 | == CSS_FRAME_FLASH_STATE_FULL) { | |
1105 | asd->frame_status[vb->i] = | |
1106 | ATOMISP_FRAME_STATUS_FLASH_EXPOSED; | |
1107 | asd->params.num_flash_frames--; | |
1108 | dev_dbg(isp->dev, "%s completely flashed\n", | |
1109 | __func__); | |
1110 | } else { | |
1111 | asd->frame_status[vb->i] = | |
1112 | ATOMISP_FRAME_STATUS_OK; | |
1113 | dev_dbg(isp->dev, | |
1114 | "%s no flash in this frame\n", | |
1115 | __func__); | |
1116 | } | |
1117 | ||
1118 | /* Check if flashing sequence is done */ | |
1119 | if (asd->frame_status[vb->i] == | |
1120 | ATOMISP_FRAME_STATUS_FLASH_EXPOSED) | |
1121 | asd->params.flash_state = ATOMISP_FLASH_DONE; | |
1122 | } else if (isp->flash) { | |
1123 | if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == | |
1124 | 0 && ctrl.value == ATOMISP_FLASH_MODE_TORCH) { | |
1125 | ctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY; | |
1126 | if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) | |
1127 | == 0 && ctrl.value > 0) { | |
1128 | asd->frame_status[vb->i] = | |
1129 | ATOMISP_FRAME_STATUS_FLASH_EXPOSED; | |
1130 | } else { | |
1131 | asd->frame_status[vb->i] = | |
1132 | ATOMISP_FRAME_STATUS_OK; | |
1133 | } | |
1134 | } else | |
1135 | asd->frame_status[vb->i] = | |
1136 | ATOMISP_FRAME_STATUS_OK; | |
1137 | } else { | |
1138 | asd->frame_status[vb->i] = ATOMISP_FRAME_STATUS_OK; | |
1139 | } | |
1140 | ||
1141 | asd->params.last_frame_status = asd->frame_status[vb->i]; | |
1142 | ||
1143 | if (asd->continuous_mode->val) { | |
1144 | if (css_pipe_id == CSS_PIPE_ID_PREVIEW || | |
1145 | css_pipe_id == CSS_PIPE_ID_VIDEO) { | |
1146 | asd->latest_preview_exp_id = frame->exp_id; | |
1147 | } else if (css_pipe_id == | |
1148 | CSS_PIPE_ID_CAPTURE) { | |
1149 | if (asd->run_mode->val == | |
1150 | ATOMISP_RUN_MODE_VIDEO) | |
1151 | dev_dbg(isp->dev, "SDV capture raw buffer id: %u\n", | |
1152 | frame->exp_id); | |
1153 | else | |
1154 | dev_dbg(isp->dev, "ZSL capture raw buffer id: %u\n", | |
1155 | frame->exp_id); | |
1156 | } | |
1157 | } | |
1158 | /* | |
1159 | * Only after enabled the raw buffer lock | |
1160 | * and in continuous mode. | |
1161 | * in preview/video pipe, each buffer will | |
1162 | * be locked automatically, so record it here. | |
1163 | */ | |
1164 | if (((css_pipe_id == CSS_PIPE_ID_PREVIEW) || | |
4995706d DY |
1165 | (css_pipe_id == CSS_PIPE_ID_VIDEO)) && |
1166 | asd->enable_raw_buffer_lock->val && | |
1167 | asd->continuous_mode->val) { | |
a49d2536 AC |
1168 | atomisp_set_raw_buffer_bitmap(asd, frame->exp_id); |
1169 | WARN_ON(frame->exp_id > ATOMISP_MAX_EXP_ID); | |
1170 | } | |
1171 | ||
1172 | if (asd->params.css_update_params_needed) { | |
1173 | atomisp_apply_css_parameters(asd, | |
4995706d | 1174 | &asd->params.css_param); |
a49d2536 AC |
1175 | if (asd->params.css_param.update_flag.dz_config) |
1176 | atomisp_css_set_dz_config(asd, | |
1177 | &asd->params.css_param.dz_config); | |
1178 | /* New global dvs 6axis config should be blocked | |
1179 | * here if there's a buffer with per-frame parameters | |
1180 | * pending in CSS frame buffer queue. | |
1181 | * This is to aviod zooming vibration since global | |
1182 | * parameters take effect immediately while | |
1183 | * per-frame parameters are taken after previous | |
1184 | * buffers in CSS got processed. | |
1185 | */ | |
1186 | if (asd->params.dvs_6axis) | |
1187 | atomisp_css_set_dvs_6axis(asd, | |
1188 | asd->params.dvs_6axis); | |
1189 | else | |
1190 | asd->params.css_update_params_needed = false; | |
1191 | /* The update flag should not be cleaned here | |
1192 | * since it is still going to be used to make up | |
1193 | * following per-frame parameters. | |
1194 | * This will introduce more copy work since each | |
1195 | * time when updating global parameters, the whole | |
1196 | * parameter set are applied. | |
1197 | * FIXME: A new set of parameter copy functions can | |
1198 | * be added to make up per-frame parameters based on | |
1199 | * solid structures stored in asd->params.css_param | |
1200 | * instead of using shadow pointers in update flag. | |
1201 | */ | |
1202 | atomisp_css_update_isp_params(asd); | |
1203 | } | |
1204 | break; | |
1205 | default: | |
1206 | break; | |
1207 | } | |
1208 | if (vb) { | |
1209 | get_buf_timestamp(&vb->ts); | |
1210 | vb->field_count = atomic_read(&asd->sequence) << 1; | |
1211 | /*mark videobuffer done for dequeue*/ | |
1212 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
1213 | vb->state = !error ? VIDEOBUF_DONE : VIDEOBUF_ERROR; | |
1214 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
1215 | ||
1216 | /* | |
1217 | * Frame capture done, wake up any process block on | |
1218 | * current active buffer | |
1219 | * possibly hold by videobuf_dqbuf() | |
1220 | */ | |
1221 | wake_up(&vb->done); | |
1222 | } | |
cc4e33d9 | 1223 | #ifdef ISP2401 |
a49d2536 AC |
1224 | atomic_set(&pipe->wdt_count, 0); |
1225 | #endif | |
1226 | /* | |
1227 | * Requeue should only be done for 3a and dis buffers. | |
1228 | * Queue/dequeue order will change if driver recycles image buffers. | |
1229 | */ | |
1230 | if (requeue) { | |
1231 | err = atomisp_css_queue_buffer(asd, | |
4995706d DY |
1232 | stream_id, css_pipe_id, |
1233 | buf_type, &buffer); | |
a49d2536 AC |
1234 | if (err) |
1235 | dev_err(isp->dev, "%s, q to css fails: %d\n", | |
1236 | __func__, err); | |
1237 | return; | |
1238 | } | |
1239 | if (!error && q_buffers) | |
1240 | atomisp_qbuffers_to_css(asd); | |
1241 | #ifdef ISP2401 | |
1242 | ||
1243 | /* If there are no buffers queued then | |
1244 | * delete wdt timer. */ | |
1245 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
1246 | return; | |
1247 | if (!atomisp_buffers_queued_pipe(pipe)) | |
1248 | atomisp_wdt_stop_pipe(pipe, false); | |
1249 | else if (reset_wdt_timer) | |
1250 | /* SOF irq should not reset wdt timer. */ | |
1251 | atomisp_wdt_refresh_pipe(pipe, | |
4995706d | 1252 | ATOMISP_WDT_KEEP_CURRENT_DELAY); |
a49d2536 AC |
1253 | #endif |
1254 | } | |
1255 | ||
1256 | void atomisp_delayed_init_work(struct work_struct *work) | |
1257 | { | |
1258 | struct atomisp_sub_device *asd = container_of(work, | |
1259 | struct atomisp_sub_device, | |
1260 | delayed_init_work); | |
1261 | /* | |
1262 | * to SOC camera, use yuvpp pipe and no support continuous mode. | |
1263 | */ | |
1264 | if (!ATOMISP_USE_YUVPP(asd)) { | |
1265 | struct v4l2_event event = {0}; | |
1266 | ||
1267 | atomisp_css_allocate_continuous_frames(false, asd); | |
1268 | atomisp_css_update_continuous_frames(asd); | |
1269 | ||
1270 | event.type = V4L2_EVENT_ATOMISP_RAW_BUFFERS_ALLOC_DONE; | |
1271 | v4l2_event_queue(asd->subdev.devnode, &event); | |
1272 | } | |
1273 | ||
1274 | /* signal streamon after delayed init is done */ | |
1275 | asd->delayed_init = ATOMISP_DELAYED_INIT_DONE; | |
1276 | complete(&asd->init_done); | |
1277 | } | |
1278 | ||
1279 | static void __atomisp_css_recover(struct atomisp_device *isp, bool isp_timeout) | |
1280 | { | |
1281 | enum atomisp_css_pipe_id css_pipe_id; | |
1282 | bool stream_restart[MAX_STREAM_NUM] = {0}; | |
1283 | bool depth_mode = false; | |
1284 | int i, ret, depth_cnt = 0; | |
1285 | ||
1286 | if (!isp->sw_contex.file_input) | |
1287 | atomisp_css_irq_enable(isp, | |
4995706d | 1288 | CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); |
a49d2536 AC |
1289 | |
1290 | BUG_ON(isp->num_of_streams > MAX_STREAM_NUM); | |
1291 | ||
1292 | for (i = 0; i < isp->num_of_streams; i++) { | |
1293 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1294 | struct ia_css_pipeline *acc_pipeline; | |
1295 | struct ia_css_pipe *acc_pipe = NULL; | |
1296 | ||
1297 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED && | |
1298 | !asd->stream_prepared) | |
1299 | continue; | |
1300 | ||
1301 | /* | |
1302 | * AtomISP::waitStageUpdate is blocked when WDT happens. | |
1303 | * By calling acc_done() for all loaded fw_handles, | |
1304 | * HAL will be unblocked. | |
1305 | */ | |
1306 | acc_pipe = asd->stream_env[i].pipes[CSS_PIPE_ID_ACC]; | |
1307 | if (acc_pipe != NULL) { | |
1308 | acc_pipeline = ia_css_pipe_get_pipeline(acc_pipe); | |
1309 | if (acc_pipeline) { | |
1310 | struct ia_css_pipeline_stage *stage; | |
1311 | for (stage = acc_pipeline->stages; stage; | |
1312 | stage = stage->next) { | |
1313 | const struct ia_css_fw_info *fw; | |
1314 | fw = stage->firmware; | |
1315 | atomisp_acc_done(asd, fw->handle); | |
1316 | } | |
1317 | } | |
1318 | } | |
1319 | ||
1320 | depth_cnt++; | |
1321 | ||
1322 | if (asd->delayed_init == ATOMISP_DELAYED_INIT_QUEUED) | |
1323 | cancel_work_sync(&asd->delayed_init_work); | |
1324 | ||
1325 | complete(&asd->init_done); | |
1326 | asd->delayed_init = ATOMISP_DELAYED_INIT_NOT_QUEUED; | |
1327 | ||
1328 | stream_restart[asd->index] = true; | |
1329 | ||
1330 | asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING; | |
1331 | ||
1332 | /* stream off sensor */ | |
1333 | ret = v4l2_subdev_call( | |
1334 | isp->inputs[asd->input_curr]. | |
1335 | camera, video, s_stream, 0); | |
1336 | if (ret) | |
1337 | dev_warn(isp->dev, | |
1338 | "can't stop streaming on sensor!\n"); | |
1339 | ||
1340 | atomisp_acc_unload_extensions(asd); | |
1341 | ||
1342 | atomisp_clear_css_buffer_counters(asd); | |
1343 | ||
1344 | css_pipe_id = atomisp_get_css_pipe_id(asd); | |
1345 | atomisp_css_stop(asd, css_pipe_id, true); | |
1346 | ||
1347 | asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; | |
1348 | ||
1349 | asd->preview_exp_id = 1; | |
1350 | asd->postview_exp_id = 1; | |
1351 | /* notify HAL the CSS reset */ | |
1352 | dev_dbg(isp->dev, | |
1353 | "send reset event to %s\n", asd->subdev.devnode->name); | |
1354 | atomisp_reset_event(asd); | |
1355 | } | |
1356 | ||
1357 | /* clear irq */ | |
287666df | 1358 | disable_isp_irq(hrt_isp_css_irq_sp); |
a49d2536 AC |
1359 | clear_isp_irq(hrt_isp_css_irq_sp); |
1360 | ||
1361 | /* Set the SRSE to 3 before resetting */ | |
1362 | pci_write_config_dword(isp->pdev, PCI_I_CONTROL, isp->saved_regs.i_control | | |
4995706d | 1363 | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); |
a49d2536 AC |
1364 | |
1365 | /* reset ISP and restore its state */ | |
1366 | isp->isp_timeout = true; | |
1367 | atomisp_reset(isp); | |
1368 | isp->isp_timeout = false; | |
1369 | ||
1370 | if (!isp_timeout) { | |
1371 | for (i = 0; i < isp->num_of_streams; i++) { | |
1372 | if (isp->asd[i].depth_mode->val) | |
1373 | return; | |
1374 | } | |
1375 | } | |
1376 | ||
1377 | for (i = 0; i < isp->num_of_streams; i++) { | |
1378 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1379 | ||
1380 | if (!stream_restart[i]) | |
1381 | continue; | |
1382 | ||
1383 | if (isp->inputs[asd->input_curr].type != FILE_INPUT) | |
1384 | atomisp_css_input_set_mode(asd, | |
1385 | CSS_INPUT_MODE_SENSOR); | |
1386 | ||
1387 | css_pipe_id = atomisp_get_css_pipe_id(asd); | |
1388 | if (atomisp_css_start(asd, css_pipe_id, true)) | |
1389 | dev_warn(isp->dev, | |
1390 | "start SP failed, so do not set streaming to be enable!\n"); | |
1391 | else | |
1392 | asd->streaming = ATOMISP_DEVICE_STREAMING_ENABLED; | |
1393 | ||
1394 | atomisp_csi2_configure(asd); | |
1395 | } | |
1396 | ||
1397 | if (!isp->sw_contex.file_input) { | |
1398 | atomisp_css_irq_enable(isp, CSS_IRQ_INFO_CSS_RECEIVER_SOF, | |
1399 | atomisp_css_valid_sof(isp)); | |
1400 | ||
1401 | if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, true) < 0) | |
1402 | dev_dbg(isp->dev, "dfs failed!\n"); | |
1403 | } else { | |
1404 | if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_MAX, true) < 0) | |
1405 | dev_dbg(isp->dev, "dfs failed!\n"); | |
1406 | } | |
1407 | ||
1408 | for (i = 0; i < isp->num_of_streams; i++) { | |
1409 | struct atomisp_sub_device *asd; | |
1410 | ||
1411 | asd = &isp->asd[i]; | |
1412 | ||
1413 | if (!stream_restart[i]) | |
1414 | continue; | |
1415 | ||
1416 | if (asd->continuous_mode->val && | |
1417 | asd->delayed_init == ATOMISP_DELAYED_INIT_NOT_QUEUED) { | |
1418 | reinit_completion(&asd->init_done); | |
1419 | asd->delayed_init = ATOMISP_DELAYED_INIT_QUEUED; | |
1420 | queue_work(asd->delayed_init_workq, | |
1421 | &asd->delayed_init_work); | |
1422 | } | |
1423 | /* | |
1424 | * dequeueing buffers is not needed. CSS will recycle | |
1425 | * buffers that it has. | |
1426 | */ | |
1427 | atomisp_flush_bufs_and_wakeup(asd); | |
1428 | ||
1429 | /* Requeue unprocessed per-frame parameters. */ | |
1430 | atomisp_recover_params_queue(&asd->video_out_capture); | |
1431 | atomisp_recover_params_queue(&asd->video_out_preview); | |
1432 | atomisp_recover_params_queue(&asd->video_out_video_capture); | |
1433 | ||
1434 | if ((asd->depth_mode->val) && | |
4995706d | 1435 | (depth_cnt == ATOMISP_DEPTH_SENSOR_STREAMON_COUNT)) { |
a49d2536 AC |
1436 | depth_mode = true; |
1437 | continue; | |
1438 | } | |
1439 | ||
1440 | ret = v4l2_subdev_call( | |
1441 | isp->inputs[asd->input_curr].camera, video, | |
1442 | s_stream, 1); | |
1443 | if (ret) | |
1444 | dev_warn(isp->dev, | |
4995706d | 1445 | "can't start streaming on sensor!\n"); |
a49d2536 AC |
1446 | |
1447 | } | |
1448 | ||
1449 | if (depth_mode) { | |
1450 | if (atomisp_stream_on_master_slave_sensor(isp, true)) | |
1451 | dev_warn(isp->dev, | |
1452 | "master slave sensor stream on failed!\n"); | |
1453 | } | |
1454 | } | |
1455 | ||
1456 | void atomisp_wdt_work(struct work_struct *work) | |
1457 | { | |
1458 | struct atomisp_device *isp = container_of(work, struct atomisp_device, | |
1459 | wdt_work); | |
1460 | int i; | |
1461 | #ifdef ISP2401 | |
1462 | unsigned int pipe_wdt_cnt[MAX_STREAM_NUM][4] = { {0} }; | |
1463 | bool css_recover = true; | |
1464 | #endif | |
1465 | ||
1466 | rt_mutex_lock(&isp->mutex); | |
1467 | if (!atomisp_streaming_count(isp)) { | |
1468 | atomic_set(&isp->wdt_work_queued, 0); | |
1469 | rt_mutex_unlock(&isp->mutex); | |
1470 | return; | |
1471 | } | |
1472 | ||
1473 | #ifndef ISP2401 | |
1474 | dev_err(isp->dev, "timeout %d of %d\n", | |
1475 | atomic_read(&isp->wdt_count) + 1, | |
1476 | ATOMISP_ISP_MAX_TIMEOUT_COUNT); | |
1477 | #else | |
1478 | for (i = 0; i < isp->num_of_streams; i++) { | |
1479 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1480 | pipe_wdt_cnt[i][0] += | |
1481 | atomic_read(&asd->video_out_capture.wdt_count); | |
1482 | pipe_wdt_cnt[i][1] += | |
1483 | atomic_read(&asd->video_out_vf.wdt_count); | |
1484 | pipe_wdt_cnt[i][2] += | |
1485 | atomic_read(&asd->video_out_preview.wdt_count); | |
1486 | pipe_wdt_cnt[i][3] += | |
1487 | atomic_read(&asd->video_out_video_capture.wdt_count); | |
1488 | css_recover = | |
1489 | (pipe_wdt_cnt[i][0] <= ATOMISP_ISP_MAX_TIMEOUT_COUNT && | |
4995706d DY |
1490 | pipe_wdt_cnt[i][1] <= ATOMISP_ISP_MAX_TIMEOUT_COUNT && |
1491 | pipe_wdt_cnt[i][2] <= ATOMISP_ISP_MAX_TIMEOUT_COUNT && | |
1492 | pipe_wdt_cnt[i][3] <= ATOMISP_ISP_MAX_TIMEOUT_COUNT) | |
1493 | ? true : false; | |
a49d2536 AC |
1494 | dev_err(isp->dev, "pipe on asd%d timeout cnt: (%d, %d, %d, %d) of %d, recover = %d\n", |
1495 | asd->index, pipe_wdt_cnt[i][0], pipe_wdt_cnt[i][1], | |
1496 | pipe_wdt_cnt[i][2], pipe_wdt_cnt[i][3], | |
1497 | ATOMISP_ISP_MAX_TIMEOUT_COUNT, css_recover); | |
1498 | } | |
1499 | #endif | |
1500 | ||
1501 | #ifndef ISP2401 | |
1502 | if (atomic_inc_return(&isp->wdt_count) < | |
4995706d | 1503 | ATOMISP_ISP_MAX_TIMEOUT_COUNT) { |
a49d2536 AC |
1504 | #else |
1505 | if (css_recover) { | |
1506 | #endif | |
1507 | unsigned int old_dbglevel = dbg_level; | |
1508 | atomisp_css_debug_dump_sp_sw_debug_info(); | |
1509 | atomisp_css_debug_dump_debug_info(__func__); | |
1510 | dbg_level = old_dbglevel; | |
1511 | for (i = 0; i < isp->num_of_streams; i++) { | |
1512 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1513 | ||
1514 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
1515 | continue; | |
1516 | dev_err(isp->dev, "%s, vdev %s buffers in css: %d\n", | |
1517 | __func__, | |
1518 | asd->video_out_capture.vdev.name, | |
1519 | asd->video_out_capture. | |
1520 | buffers_in_css); | |
1521 | dev_err(isp->dev, | |
1522 | "%s, vdev %s buffers in css: %d\n", | |
1523 | __func__, | |
1524 | asd->video_out_vf.vdev.name, | |
1525 | asd->video_out_vf. | |
1526 | buffers_in_css); | |
1527 | dev_err(isp->dev, | |
1528 | "%s, vdev %s buffers in css: %d\n", | |
1529 | __func__, | |
1530 | asd->video_out_preview.vdev.name, | |
1531 | asd->video_out_preview. | |
1532 | buffers_in_css); | |
1533 | dev_err(isp->dev, | |
1534 | "%s, vdev %s buffers in css: %d\n", | |
1535 | __func__, | |
1536 | asd->video_out_video_capture.vdev.name, | |
1537 | asd->video_out_video_capture. | |
1538 | buffers_in_css); | |
1539 | dev_err(isp->dev, | |
1540 | "%s, s3a buffers in css preview pipe:%d\n", | |
1541 | __func__, | |
1542 | asd->s3a_bufs_in_css[CSS_PIPE_ID_PREVIEW]); | |
1543 | dev_err(isp->dev, | |
1544 | "%s, s3a buffers in css capture pipe:%d\n", | |
1545 | __func__, | |
1546 | asd->s3a_bufs_in_css[CSS_PIPE_ID_CAPTURE]); | |
1547 | dev_err(isp->dev, | |
1548 | "%s, s3a buffers in css video pipe:%d\n", | |
1549 | __func__, | |
1550 | asd->s3a_bufs_in_css[CSS_PIPE_ID_VIDEO]); | |
1551 | dev_err(isp->dev, | |
1552 | "%s, dis buffers in css: %d\n", | |
1553 | __func__, asd->dis_bufs_in_css); | |
1554 | dev_err(isp->dev, | |
1555 | "%s, metadata buffers in css preview pipe:%d\n", | |
1556 | __func__, | |
1557 | asd->metadata_bufs_in_css | |
1558 | [ATOMISP_INPUT_STREAM_GENERAL] | |
1559 | [CSS_PIPE_ID_PREVIEW]); | |
1560 | dev_err(isp->dev, | |
1561 | "%s, metadata buffers in css capture pipe:%d\n", | |
1562 | __func__, | |
1563 | asd->metadata_bufs_in_css | |
1564 | [ATOMISP_INPUT_STREAM_GENERAL] | |
1565 | [CSS_PIPE_ID_CAPTURE]); | |
1566 | dev_err(isp->dev, | |
1567 | "%s, metadata buffers in css video pipe:%d\n", | |
1568 | __func__, | |
1569 | asd->metadata_bufs_in_css | |
1570 | [ATOMISP_INPUT_STREAM_GENERAL] | |
1571 | [CSS_PIPE_ID_VIDEO]); | |
1572 | if (asd->enable_raw_buffer_lock->val) { | |
1573 | unsigned int j; | |
1574 | ||
1575 | dev_err(isp->dev, "%s, raw_buffer_locked_count %d\n", | |
1576 | __func__, asd->raw_buffer_locked_count); | |
1577 | for (j = 0; j <= ATOMISP_MAX_EXP_ID/32; j++) | |
1578 | dev_err(isp->dev, "%s, raw_buffer_bitmap[%d]: 0x%x\n", | |
1579 | __func__, j, | |
1580 | asd->raw_buffer_bitmap[j]); | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | /*sh_css_dump_sp_state();*/ | |
1585 | /*sh_css_dump_isp_state();*/ | |
1586 | } else { | |
1587 | for (i = 0; i < isp->num_of_streams; i++) { | |
1588 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1589 | if (asd->streaming == | |
1590 | ATOMISP_DEVICE_STREAMING_ENABLED) { | |
1591 | atomisp_clear_css_buffer_counters(asd); | |
1592 | atomisp_flush_bufs_and_wakeup(asd); | |
1593 | complete(&asd->init_done); | |
1594 | } | |
1595 | #ifdef ISP2401 | |
1596 | atomisp_wdt_stop(asd, false); | |
1597 | #endif | |
1598 | } | |
1599 | ||
1600 | #ifndef ISP2401 | |
1601 | atomic_set(&isp->wdt_count, 0); | |
1602 | #endif | |
1603 | isp->isp_fatal_error = true; | |
1604 | atomic_set(&isp->wdt_work_queued, 0); | |
1605 | ||
1606 | rt_mutex_unlock(&isp->mutex); | |
1607 | return; | |
1608 | } | |
1609 | ||
1610 | __atomisp_css_recover(isp, true); | |
1611 | #ifdef ISP2401 | |
1612 | for (i = 0; i < isp->num_of_streams; i++) { | |
1613 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1614 | if (asd->streaming == | |
1615 | ATOMISP_DEVICE_STREAMING_ENABLED) { | |
1616 | atomisp_wdt_refresh(asd, | |
1617 | isp->sw_contex.file_input ? | |
1618 | ATOMISP_ISP_FILE_TIMEOUT_DURATION : | |
1619 | ATOMISP_ISP_TIMEOUT_DURATION); | |
1620 | } | |
1621 | } | |
1622 | #endif | |
a49d2536 AC |
1623 | dev_err(isp->dev, "timeout recovery handling done\n"); |
1624 | atomic_set(&isp->wdt_work_queued, 0); | |
1625 | ||
1626 | rt_mutex_unlock(&isp->mutex); | |
1627 | } | |
1628 | ||
1629 | void atomisp_css_flush(struct atomisp_device *isp) | |
1630 | { | |
1631 | int i; | |
1632 | ||
1633 | if (!atomisp_streaming_count(isp)) | |
1634 | return; | |
1635 | ||
1636 | /* Disable wdt */ | |
1637 | for (i = 0; i < isp->num_of_streams; i++) { | |
1638 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1639 | atomisp_wdt_stop(asd, true); | |
1640 | } | |
1641 | ||
1642 | /* Start recover */ | |
1643 | __atomisp_css_recover(isp, false); | |
1644 | /* Restore wdt */ | |
1645 | for (i = 0; i < isp->num_of_streams; i++) { | |
1646 | struct atomisp_sub_device *asd = &isp->asd[i]; | |
1647 | ||
1648 | if (asd->streaming != | |
1649 | ATOMISP_DEVICE_STREAMING_ENABLED) | |
1650 | continue; | |
1651 | ||
1652 | atomisp_wdt_refresh(asd, | |
4995706d DY |
1653 | isp->sw_contex.file_input ? |
1654 | ATOMISP_ISP_FILE_TIMEOUT_DURATION : | |
1655 | ATOMISP_ISP_TIMEOUT_DURATION); | |
a49d2536 AC |
1656 | } |
1657 | dev_dbg(isp->dev, "atomisp css flush done\n"); | |
1658 | } | |
1659 | ||
ef674997 | 1660 | void atomisp_wdt(struct timer_list *t) |
a49d2536 AC |
1661 | { |
1662 | #ifndef ISP2401 | |
ef674997 | 1663 | struct atomisp_sub_device *asd = from_timer(asd, t, wdt); |
a49d2536 | 1664 | #else |
ef674997 | 1665 | struct atomisp_video_pipe *pipe = from_timer(pipe, t, wdt); |
a49d2536 | 1666 | struct atomisp_sub_device *asd = pipe->asd; |
a49d2536 | 1667 | #endif |
ef674997 | 1668 | struct atomisp_device *isp = asd->isp; |
a49d2536 AC |
1669 | |
1670 | #ifdef ISP2401 | |
1671 | atomic_inc(&pipe->wdt_count); | |
1672 | dev_warn(isp->dev, | |
1673 | "[WARNING]asd %d pipe %s ISP timeout %d!\n", | |
1674 | asd->index, pipe->vdev.name, | |
1675 | atomic_read(&pipe->wdt_count)); | |
1676 | #endif | |
1677 | if (atomic_read(&isp->wdt_work_queued)) { | |
1678 | dev_dbg(isp->dev, "ISP watchdog was put into workqueue\n"); | |
1679 | return; | |
1680 | } | |
1681 | atomic_set(&isp->wdt_work_queued, 1); | |
1682 | queue_work(isp->wdt_work_queue, &isp->wdt_work); | |
1683 | } | |
1684 | ||
1685 | #ifndef ISP2401 | |
1686 | void atomisp_wdt_refresh(struct atomisp_sub_device *asd, unsigned int delay) | |
1687 | #else | |
1688 | void atomisp_wdt_refresh_pipe(struct atomisp_video_pipe *pipe, | |
1689 | unsigned int delay) | |
1690 | #endif | |
1691 | { | |
1692 | unsigned long next; | |
1693 | ||
1694 | if (delay != ATOMISP_WDT_KEEP_CURRENT_DELAY) | |
1695 | #ifndef ISP2401 | |
1696 | asd->wdt_duration = delay; | |
1697 | #else | |
1698 | pipe->wdt_duration = delay; | |
1699 | #endif | |
1700 | ||
1701 | #ifndef ISP2401 | |
1702 | next = jiffies + asd->wdt_duration; | |
1703 | #else | |
1704 | next = jiffies + pipe->wdt_duration; | |
1705 | #endif | |
1706 | ||
1707 | /* Override next if it has been pushed beyon the "next" time */ | |
1708 | #ifndef ISP2401 | |
1709 | if (atomisp_is_wdt_running(asd) && time_after(asd->wdt_expires, next)) | |
1710 | next = asd->wdt_expires; | |
1711 | #else | |
1712 | if (atomisp_is_wdt_running(pipe) && time_after(pipe->wdt_expires, next)) | |
1713 | next = pipe->wdt_expires; | |
1714 | #endif | |
1715 | ||
1716 | #ifndef ISP2401 | |
1717 | asd->wdt_expires = next; | |
1718 | #else | |
1719 | pipe->wdt_expires = next; | |
1720 | #endif | |
1721 | ||
1722 | #ifndef ISP2401 | |
1723 | if (atomisp_is_wdt_running(asd)) | |
1724 | dev_dbg(asd->isp->dev, "WDT will hit after %d ms\n", | |
1725 | ((int)(next - jiffies) * 1000 / HZ)); | |
1726 | #else | |
1727 | if (atomisp_is_wdt_running(pipe)) | |
1728 | dev_dbg(pipe->asd->isp->dev, "WDT will hit after %d ms (%s)\n", | |
1729 | ((int)(next - jiffies) * 1000 / HZ), pipe->vdev.name); | |
1730 | #endif | |
1731 | else | |
1732 | #ifndef ISP2401 | |
1733 | dev_dbg(asd->isp->dev, "WDT starts with %d ms period\n", | |
1734 | ((int)(next - jiffies) * 1000 / HZ)); | |
1735 | #else | |
1736 | dev_dbg(pipe->asd->isp->dev, "WDT starts with %d ms period (%s)\n", | |
1737 | ((int)(next - jiffies) * 1000 / HZ), pipe->vdev.name); | |
1738 | #endif | |
1739 | ||
1740 | #ifndef ISP2401 | |
1741 | mod_timer(&asd->wdt, next); | |
1742 | atomic_set(&asd->isp->wdt_count, 0); | |
1743 | #else | |
1744 | mod_timer(&pipe->wdt, next); | |
1745 | #endif | |
1746 | } | |
1747 | ||
1748 | #ifndef ISP2401 | |
1749 | void atomisp_wdt_stop(struct atomisp_sub_device *asd, bool sync) | |
1750 | #else | |
1751 | void atomisp_wdt_refresh(struct atomisp_sub_device *asd, unsigned int delay) | |
1752 | { | |
1753 | dev_dbg(asd->isp->dev, "WDT refresh all:\n"); | |
1754 | if (atomisp_is_wdt_running(&asd->video_out_capture)) | |
1755 | atomisp_wdt_refresh_pipe(&asd->video_out_capture, delay); | |
1756 | if (atomisp_is_wdt_running(&asd->video_out_preview)) | |
1757 | atomisp_wdt_refresh_pipe(&asd->video_out_preview, delay); | |
1758 | if (atomisp_is_wdt_running(&asd->video_out_vf)) | |
1759 | atomisp_wdt_refresh_pipe(&asd->video_out_vf, delay); | |
1760 | if (atomisp_is_wdt_running(&asd->video_out_video_capture)) | |
1761 | atomisp_wdt_refresh_pipe(&asd->video_out_video_capture, delay); | |
1762 | } | |
1763 | ||
1764 | ||
1765 | void atomisp_wdt_stop_pipe(struct atomisp_video_pipe *pipe, bool sync) | |
1766 | #endif | |
1767 | { | |
1768 | #ifndef ISP2401 | |
1769 | dev_dbg(asd->isp->dev, "WDT stop\n"); | |
1770 | #else | |
1771 | if (!atomisp_is_wdt_running(pipe)) | |
1772 | return; | |
1773 | ||
1774 | dev_dbg(pipe->asd->isp->dev, | |
1775 | "WDT stop asd %d (%s)\n", pipe->asd->index, pipe->vdev.name); | |
1776 | ||
1777 | #endif | |
1778 | if (sync) { | |
1779 | #ifndef ISP2401 | |
1780 | del_timer_sync(&asd->wdt); | |
1781 | cancel_work_sync(&asd->isp->wdt_work); | |
1782 | #else | |
1783 | del_timer_sync(&pipe->wdt); | |
1784 | cancel_work_sync(&pipe->asd->isp->wdt_work); | |
1785 | #endif | |
1786 | } else { | |
1787 | #ifndef ISP2401 | |
1788 | del_timer(&asd->wdt); | |
1789 | #else | |
1790 | del_timer(&pipe->wdt); | |
1791 | #endif | |
1792 | } | |
1793 | } | |
1794 | ||
1795 | #ifndef ISP2401 | |
1796 | void atomisp_wdt_start(struct atomisp_sub_device *asd) | |
1797 | #else | |
1798 | void atomisp_wdt_stop(struct atomisp_sub_device *asd, bool sync) | |
1799 | { | |
1800 | dev_dbg(asd->isp->dev, "WDT stop all:\n"); | |
1801 | atomisp_wdt_stop_pipe(&asd->video_out_capture, sync); | |
1802 | atomisp_wdt_stop_pipe(&asd->video_out_preview, sync); | |
1803 | atomisp_wdt_stop_pipe(&asd->video_out_vf, sync); | |
1804 | atomisp_wdt_stop_pipe(&asd->video_out_video_capture, sync); | |
1805 | } | |
1806 | ||
1807 | void atomisp_wdt_start(struct atomisp_video_pipe *pipe) | |
1808 | #endif | |
1809 | { | |
1810 | #ifndef ISP2401 | |
1811 | atomisp_wdt_refresh(asd, ATOMISP_ISP_TIMEOUT_DURATION); | |
1812 | #else | |
1813 | atomisp_wdt_refresh_pipe(pipe, ATOMISP_ISP_TIMEOUT_DURATION); | |
1814 | #endif | |
1815 | } | |
1816 | ||
1817 | void atomisp_setup_flash(struct atomisp_sub_device *asd) | |
1818 | { | |
1819 | struct atomisp_device *isp = asd->isp; | |
1820 | struct v4l2_control ctrl; | |
1821 | ||
1822 | if (isp->flash == NULL) | |
1823 | return; | |
1824 | ||
1825 | if (asd->params.flash_state != ATOMISP_FLASH_REQUESTED && | |
1826 | asd->params.flash_state != ATOMISP_FLASH_DONE) | |
1827 | return; | |
1828 | ||
1829 | if (asd->params.num_flash_frames) { | |
1830 | /* make sure the timeout is set before setting flash mode */ | |
1831 | ctrl.id = V4L2_CID_FLASH_TIMEOUT; | |
1832 | ctrl.value = FLASH_TIMEOUT; | |
1833 | ||
1834 | if (v4l2_s_ctrl(NULL, isp->flash->ctrl_handler, &ctrl)) { | |
1835 | dev_err(isp->dev, "flash timeout configure failed\n"); | |
1836 | return; | |
1837 | } | |
1838 | ||
1839 | atomisp_css_request_flash(asd); | |
1840 | asd->params.flash_state = ATOMISP_FLASH_ONGOING; | |
1841 | } else { | |
1842 | asd->params.flash_state = ATOMISP_FLASH_IDLE; | |
1843 | } | |
1844 | } | |
1845 | ||
1846 | irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) | |
1847 | { | |
1848 | struct atomisp_device *isp = isp_ptr; | |
1849 | unsigned long flags; | |
1850 | bool frame_done_found[MAX_STREAM_NUM] = {0}; | |
1851 | bool css_pipe_done[MAX_STREAM_NUM] = {0}; | |
1852 | unsigned int i; | |
1853 | struct atomisp_sub_device *asd = &isp->asd[0]; | |
1854 | ||
1855 | dev_dbg(isp->dev, ">%s\n", __func__); | |
1856 | ||
1857 | spin_lock_irqsave(&isp->lock, flags); | |
1858 | ||
1859 | if (!atomisp_streaming_count(isp) && !atomisp_is_acc_enabled(isp)) { | |
1860 | spin_unlock_irqrestore(&isp->lock, flags); | |
1861 | return IRQ_HANDLED; | |
1862 | } | |
1863 | ||
1864 | spin_unlock_irqrestore(&isp->lock, flags); | |
1865 | ||
1866 | /* | |
1867 | * The standard CSS2.0 API tells the following calling sequence of | |
1868 | * dequeue ready buffers: | |
1869 | * while (ia_css_dequeue_event(...)) { | |
1870 | * switch (event.type) { | |
1871 | * ... | |
1872 | * ia_css_pipe_dequeue_buffer() | |
1873 | * } | |
1874 | * } | |
1875 | * That is, dequeue event and buffer are one after another. | |
1876 | * | |
1877 | * But the following implementation is to first deuque all the event | |
1878 | * to a FIFO, then process the event in the FIFO. | |
1879 | * This will not have issue in single stream mode, but it do have some | |
1880 | * issue in multiple stream case. The issue is that | |
1881 | * ia_css_pipe_dequeue_buffer() will not return the corrent buffer in | |
1882 | * a specific pipe. | |
1883 | * | |
1884 | * This is due to ia_css_pipe_dequeue_buffer() does not take the | |
1885 | * ia_css_pipe parameter. | |
1886 | * | |
1887 | * So: | |
1888 | * For CSS2.0: we change the way to not dequeue all the event at one | |
1889 | * time, instead, dequue one and process one, then another | |
1890 | */ | |
1891 | rt_mutex_lock(&isp->mutex); | |
1892 | if (atomisp_css_isr_thread(isp, frame_done_found, css_pipe_done)) | |
1893 | goto out; | |
1894 | ||
1895 | for (i = 0; i < isp->num_of_streams; i++) { | |
1896 | asd = &isp->asd[i]; | |
1897 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
1898 | continue; | |
1899 | atomisp_setup_flash(asd); | |
1900 | ||
1901 | } | |
1902 | out: | |
1903 | rt_mutex_unlock(&isp->mutex); | |
1904 | for (i = 0; i < isp->num_of_streams; i++) { | |
1905 | asd = &isp->asd[i]; | |
1906 | if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED | |
1907 | && css_pipe_done[asd->index] | |
1908 | && isp->sw_contex.file_input) | |
1909 | v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
1910 | video, s_stream, 1); | |
1911 | /* FIXME! FIX ACC implementation */ | |
1912 | if (asd->acc.pipeline && css_pipe_done[asd->index]) | |
1913 | atomisp_css_acc_done(asd); | |
1914 | } | |
1915 | dev_dbg(isp->dev, "<%s\n", __func__); | |
1916 | ||
1917 | return IRQ_HANDLED; | |
1918 | } | |
1919 | ||
1920 | /* | |
1921 | * utils for buffer allocation/free | |
1922 | */ | |
1923 | ||
1924 | int atomisp_get_frame_pgnr(struct atomisp_device *isp, | |
1925 | const struct atomisp_css_frame *frame, u32 *p_pgnr) | |
1926 | { | |
1927 | if (!frame) { | |
1928 | dev_err(isp->dev, "%s: NULL frame pointer ERROR.\n", __func__); | |
1929 | return -EINVAL; | |
1930 | } | |
1931 | ||
1932 | *p_pgnr = DIV_ROUND_UP(frame->data_bytes, PAGE_SIZE); | |
1933 | return 0; | |
1934 | } | |
1935 | ||
1936 | /* | |
1937 | * Get internal fmt according to V4L2 fmt | |
1938 | */ | |
1939 | static enum atomisp_css_frame_format | |
1940 | v4l2_fmt_to_sh_fmt(u32 fmt) | |
1941 | { | |
1942 | switch (fmt) { | |
1943 | case V4L2_PIX_FMT_YUV420: | |
1944 | return CSS_FRAME_FORMAT_YUV420; | |
1945 | case V4L2_PIX_FMT_YVU420: | |
1946 | return CSS_FRAME_FORMAT_YV12; | |
1947 | case V4L2_PIX_FMT_YUV422P: | |
1948 | return CSS_FRAME_FORMAT_YUV422; | |
1949 | case V4L2_PIX_FMT_YUV444: | |
1950 | return CSS_FRAME_FORMAT_YUV444; | |
1951 | case V4L2_PIX_FMT_NV12: | |
1952 | return CSS_FRAME_FORMAT_NV12; | |
1953 | case V4L2_PIX_FMT_NV21: | |
1954 | return CSS_FRAME_FORMAT_NV21; | |
1955 | case V4L2_PIX_FMT_NV16: | |
1956 | return CSS_FRAME_FORMAT_NV16; | |
1957 | case V4L2_PIX_FMT_NV61: | |
1958 | return CSS_FRAME_FORMAT_NV61; | |
1959 | case V4L2_PIX_FMT_UYVY: | |
1960 | return CSS_FRAME_FORMAT_UYVY; | |
1961 | case V4L2_PIX_FMT_YUYV: | |
1962 | return CSS_FRAME_FORMAT_YUYV; | |
1963 | case V4L2_PIX_FMT_RGB24: | |
1964 | return CSS_FRAME_FORMAT_PLANAR_RGB888; | |
1965 | case V4L2_PIX_FMT_RGB32: | |
1966 | return CSS_FRAME_FORMAT_RGBA888; | |
1967 | case V4L2_PIX_FMT_RGB565: | |
1968 | return CSS_FRAME_FORMAT_RGB565; | |
1969 | case V4L2_PIX_FMT_JPEG: | |
1970 | case V4L2_PIX_FMT_CUSTOM_M10MO_RAW: | |
1971 | return CSS_FRAME_FORMAT_BINARY_8; | |
1972 | case V4L2_PIX_FMT_SBGGR16: | |
1973 | case V4L2_PIX_FMT_SBGGR10: | |
1974 | case V4L2_PIX_FMT_SGBRG10: | |
1975 | case V4L2_PIX_FMT_SGRBG10: | |
1976 | case V4L2_PIX_FMT_SRGGB10: | |
1977 | case V4L2_PIX_FMT_SBGGR12: | |
1978 | case V4L2_PIX_FMT_SGBRG12: | |
1979 | case V4L2_PIX_FMT_SGRBG12: | |
1980 | case V4L2_PIX_FMT_SRGGB12: | |
1981 | case V4L2_PIX_FMT_SBGGR8: | |
1982 | case V4L2_PIX_FMT_SGBRG8: | |
1983 | case V4L2_PIX_FMT_SGRBG8: | |
1984 | case V4L2_PIX_FMT_SRGGB8: | |
1985 | return CSS_FRAME_FORMAT_RAW; | |
1986 | default: | |
1987 | return -EINVAL; | |
1988 | } | |
1989 | } | |
1990 | /* | |
1991 | * raw format match between SH format and V4L2 format | |
1992 | */ | |
1993 | static int raw_output_format_match_input(u32 input, u32 output) | |
1994 | { | |
1995 | if ((input == CSS_FORMAT_RAW_12) && | |
1996 | ((output == V4L2_PIX_FMT_SRGGB12) || | |
1997 | (output == V4L2_PIX_FMT_SGRBG12) || | |
1998 | (output == V4L2_PIX_FMT_SBGGR12) || | |
1999 | (output == V4L2_PIX_FMT_SGBRG12))) | |
2000 | return 0; | |
2001 | ||
2002 | if ((input == CSS_FORMAT_RAW_10) && | |
2003 | ((output == V4L2_PIX_FMT_SRGGB10) || | |
2004 | (output == V4L2_PIX_FMT_SGRBG10) || | |
2005 | (output == V4L2_PIX_FMT_SBGGR10) || | |
2006 | (output == V4L2_PIX_FMT_SGBRG10))) | |
2007 | return 0; | |
2008 | ||
2009 | if ((input == CSS_FORMAT_RAW_8) && | |
2010 | ((output == V4L2_PIX_FMT_SRGGB8) || | |
2011 | (output == V4L2_PIX_FMT_SGRBG8) || | |
2012 | (output == V4L2_PIX_FMT_SBGGR8) || | |
2013 | (output == V4L2_PIX_FMT_SGBRG8))) | |
2014 | return 0; | |
2015 | ||
2016 | if ((input == CSS_FORMAT_RAW_16) && (output == V4L2_PIX_FMT_SBGGR16)) | |
2017 | return 0; | |
2018 | ||
2019 | return -EINVAL; | |
2020 | } | |
2021 | ||
2022 | static u32 get_pixel_depth(u32 pixelformat) | |
2023 | { | |
2024 | switch (pixelformat) { | |
2025 | case V4L2_PIX_FMT_YUV420: | |
2026 | case V4L2_PIX_FMT_NV12: | |
2027 | case V4L2_PIX_FMT_NV21: | |
2028 | case V4L2_PIX_FMT_YVU420: | |
2029 | return 12; | |
2030 | case V4L2_PIX_FMT_YUV422P: | |
2031 | case V4L2_PIX_FMT_YUYV: | |
2032 | case V4L2_PIX_FMT_UYVY: | |
2033 | case V4L2_PIX_FMT_NV16: | |
2034 | case V4L2_PIX_FMT_NV61: | |
2035 | case V4L2_PIX_FMT_RGB565: | |
2036 | case V4L2_PIX_FMT_SBGGR16: | |
2037 | case V4L2_PIX_FMT_SBGGR12: | |
2038 | case V4L2_PIX_FMT_SGBRG12: | |
2039 | case V4L2_PIX_FMT_SGRBG12: | |
2040 | case V4L2_PIX_FMT_SRGGB12: | |
2041 | case V4L2_PIX_FMT_SBGGR10: | |
2042 | case V4L2_PIX_FMT_SGBRG10: | |
2043 | case V4L2_PIX_FMT_SGRBG10: | |
2044 | case V4L2_PIX_FMT_SRGGB10: | |
2045 | return 16; | |
2046 | case V4L2_PIX_FMT_RGB24: | |
2047 | case V4L2_PIX_FMT_YUV444: | |
2048 | return 24; | |
2049 | case V4L2_PIX_FMT_RGB32: | |
2050 | return 32; | |
2051 | case V4L2_PIX_FMT_JPEG: | |
2052 | case V4L2_PIX_FMT_CUSTOM_M10MO_RAW: | |
2053 | case V4L2_PIX_FMT_SBGGR8: | |
2054 | case V4L2_PIX_FMT_SGBRG8: | |
2055 | case V4L2_PIX_FMT_SGRBG8: | |
2056 | case V4L2_PIX_FMT_SRGGB8: | |
2057 | return 8; | |
2058 | default: | |
2059 | return 8 * 2; /* raw type now */ | |
2060 | } | |
2061 | } | |
2062 | ||
2063 | bool atomisp_is_mbuscode_raw(uint32_t code) | |
2064 | { | |
2065 | return code >= 0x3000 && code < 0x4000; | |
2066 | } | |
2067 | ||
2068 | /* | |
2069 | * ISP features control function | |
2070 | */ | |
2071 | ||
2072 | /* | |
2073 | * Set ISP capture mode based on current settings | |
2074 | */ | |
2075 | static void atomisp_update_capture_mode(struct atomisp_sub_device *asd) | |
2076 | { | |
2077 | if (asd->params.gdc_cac_en) | |
2078 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_ADVANCED); | |
2079 | else if (asd->params.low_light) | |
2080 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_LOW_LIGHT); | |
2081 | else if (asd->video_out_capture.sh_fmt == CSS_FRAME_FORMAT_RAW) | |
2082 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_RAW); | |
2083 | else | |
2084 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_PRIMARY); | |
2085 | } | |
2086 | ||
2087 | #ifdef ISP2401 | |
2088 | int atomisp_set_sensor_runmode(struct atomisp_sub_device *asd, | |
2089 | struct atomisp_s_runmode *runmode) | |
2090 | { | |
2091 | struct atomisp_device *isp = asd->isp; | |
2092 | struct v4l2_ctrl *c; | |
2093 | struct v4l2_streamparm p = {0}; | |
2094 | int ret; | |
2095 | int modes[] = { CI_MODE_NONE, | |
2096 | CI_MODE_VIDEO, | |
2097 | CI_MODE_STILL_CAPTURE, | |
2098 | CI_MODE_CONTINUOUS, | |
2099 | CI_MODE_PREVIEW }; | |
2100 | ||
2101 | if (!(runmode && (runmode->mode & RUNMODE_MASK))) | |
2102 | return -EINVAL; | |
2103 | ||
2104 | mutex_lock(asd->ctrl_handler.lock); | |
2105 | c = v4l2_ctrl_find(isp->inputs[asd->input_curr].camera->ctrl_handler, | |
4995706d | 2106 | V4L2_CID_RUN_MODE); |
a49d2536 AC |
2107 | |
2108 | if (c) { | |
2109 | ret = v4l2_ctrl_s_ctrl(c, runmode->mode); | |
2110 | } else { | |
2111 | p.parm.capture.capturemode = modes[runmode->mode]; | |
2112 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
4995706d | 2113 | video, s_parm, &p); |
a49d2536 AC |
2114 | } |
2115 | ||
2116 | mutex_unlock(asd->ctrl_handler.lock); | |
2117 | return ret; | |
2118 | } | |
2119 | ||
2120 | #endif | |
2121 | /* | |
2122 | * Function to enable/disable lens geometry distortion correction (GDC) and | |
2123 | * chromatic aberration correction (CAC) | |
2124 | */ | |
2125 | int atomisp_gdc_cac(struct atomisp_sub_device *asd, int flag, | |
2126 | __s32 *value) | |
2127 | { | |
2128 | if (flag == 0) { | |
2129 | *value = asd->params.gdc_cac_en; | |
2130 | return 0; | |
2131 | } | |
2132 | ||
2133 | asd->params.gdc_cac_en = !!*value; | |
2134 | if (asd->params.gdc_cac_en) { | |
2135 | atomisp_css_set_morph_table(asd, | |
4995706d | 2136 | asd->params.css_param.morph_table); |
a49d2536 AC |
2137 | } else { |
2138 | atomisp_css_set_morph_table(asd, NULL); | |
2139 | } | |
2140 | asd->params.css_update_params_needed = true; | |
2141 | atomisp_update_capture_mode(asd); | |
2142 | return 0; | |
2143 | } | |
2144 | ||
2145 | /* | |
2146 | * Function to enable/disable low light mode including ANR | |
2147 | */ | |
2148 | int atomisp_low_light(struct atomisp_sub_device *asd, int flag, | |
2149 | __s32 *value) | |
2150 | { | |
2151 | if (flag == 0) { | |
2152 | *value = asd->params.low_light; | |
2153 | return 0; | |
2154 | } | |
2155 | ||
2156 | asd->params.low_light = (*value != 0); | |
2157 | atomisp_update_capture_mode(asd); | |
2158 | return 0; | |
2159 | } | |
2160 | ||
2161 | /* | |
2162 | * Function to enable/disable extra noise reduction (XNR) in low light | |
2163 | * condition | |
2164 | */ | |
2165 | int atomisp_xnr(struct atomisp_sub_device *asd, int flag, | |
2166 | int *xnr_enable) | |
2167 | { | |
2168 | if (flag == 0) { | |
2169 | *xnr_enable = asd->params.xnr_en; | |
2170 | return 0; | |
2171 | } | |
2172 | ||
2173 | atomisp_css_capture_enable_xnr(asd, !!*xnr_enable); | |
2174 | ||
2175 | return 0; | |
2176 | } | |
2177 | ||
2178 | /* | |
2179 | * Function to configure bayer noise reduction | |
2180 | */ | |
2181 | int atomisp_nr(struct atomisp_sub_device *asd, int flag, | |
2182 | struct atomisp_nr_config *arg) | |
2183 | { | |
2184 | if (flag == 0) { | |
2185 | /* Get nr config from current setup */ | |
2186 | if (atomisp_css_get_nr_config(asd, arg)) | |
2187 | return -EINVAL; | |
2188 | } else { | |
2189 | /* Set nr config to isp parameters */ | |
2190 | memcpy(&asd->params.css_param.nr_config, arg, | |
4995706d | 2191 | sizeof(struct atomisp_css_nr_config)); |
a49d2536 AC |
2192 | atomisp_css_set_nr_config(asd, &asd->params.css_param.nr_config); |
2193 | asd->params.css_update_params_needed = true; | |
2194 | } | |
2195 | return 0; | |
2196 | } | |
2197 | ||
2198 | /* | |
2199 | * Function to configure temporal noise reduction (TNR) | |
2200 | */ | |
2201 | int atomisp_tnr(struct atomisp_sub_device *asd, int flag, | |
2202 | struct atomisp_tnr_config *config) | |
2203 | { | |
2204 | /* Get tnr config from current setup */ | |
2205 | if (flag == 0) { | |
2206 | /* Get tnr config from current setup */ | |
2207 | if (atomisp_css_get_tnr_config(asd, config)) | |
2208 | return -EINVAL; | |
2209 | } else { | |
2210 | /* Set tnr config to isp parameters */ | |
2211 | memcpy(&asd->params.css_param.tnr_config, config, | |
2212 | sizeof(struct atomisp_css_tnr_config)); | |
2213 | atomisp_css_set_tnr_config(asd, &asd->params.css_param.tnr_config); | |
2214 | asd->params.css_update_params_needed = true; | |
2215 | } | |
2216 | ||
2217 | return 0; | |
2218 | } | |
2219 | ||
2220 | /* | |
2221 | * Function to configure black level compensation | |
2222 | */ | |
2223 | int atomisp_black_level(struct atomisp_sub_device *asd, int flag, | |
2224 | struct atomisp_ob_config *config) | |
2225 | { | |
2226 | if (flag == 0) { | |
2227 | /* Get ob config from current setup */ | |
2228 | if (atomisp_css_get_ob_config(asd, config)) | |
2229 | return -EINVAL; | |
2230 | } else { | |
2231 | /* Set ob config to isp parameters */ | |
2232 | memcpy(&asd->params.css_param.ob_config, config, | |
4995706d | 2233 | sizeof(struct atomisp_css_ob_config)); |
a49d2536 AC |
2234 | atomisp_css_set_ob_config(asd, &asd->params.css_param.ob_config); |
2235 | asd->params.css_update_params_needed = true; | |
2236 | } | |
2237 | ||
2238 | return 0; | |
2239 | } | |
2240 | ||
2241 | /* | |
2242 | * Function to configure edge enhancement | |
2243 | */ | |
2244 | int atomisp_ee(struct atomisp_sub_device *asd, int flag, | |
2245 | struct atomisp_ee_config *config) | |
2246 | { | |
2247 | if (flag == 0) { | |
2248 | /* Get ee config from current setup */ | |
2249 | if (atomisp_css_get_ee_config(asd, config)) | |
2250 | return -EINVAL; | |
2251 | } else { | |
2252 | /* Set ee config to isp parameters */ | |
2253 | memcpy(&asd->params.css_param.ee_config, config, | |
2254 | sizeof(asd->params.css_param.ee_config)); | |
2255 | atomisp_css_set_ee_config(asd, &asd->params.css_param.ee_config); | |
2256 | asd->params.css_update_params_needed = true; | |
2257 | } | |
2258 | ||
2259 | return 0; | |
2260 | } | |
2261 | ||
2262 | /* | |
2263 | * Function to update Gamma table for gamma, brightness and contrast config | |
2264 | */ | |
2265 | int atomisp_gamma(struct atomisp_sub_device *asd, int flag, | |
2266 | struct atomisp_gamma_table *config) | |
2267 | { | |
2268 | if (flag == 0) { | |
2269 | /* Get gamma table from current setup */ | |
2270 | if (atomisp_css_get_gamma_table(asd, config)) | |
2271 | return -EINVAL; | |
2272 | } else { | |
2273 | /* Set gamma table to isp parameters */ | |
2274 | memcpy(&asd->params.css_param.gamma_table, config, | |
2275 | sizeof(asd->params.css_param.gamma_table)); | |
2276 | atomisp_css_set_gamma_table(asd, &asd->params.css_param.gamma_table); | |
2277 | } | |
2278 | ||
2279 | return 0; | |
2280 | } | |
2281 | ||
2282 | /* | |
2283 | * Function to update Ctc table for Chroma Enhancement | |
2284 | */ | |
2285 | int atomisp_ctc(struct atomisp_sub_device *asd, int flag, | |
2286 | struct atomisp_ctc_table *config) | |
2287 | { | |
2288 | if (flag == 0) { | |
2289 | /* Get ctc table from current setup */ | |
2290 | if (atomisp_css_get_ctc_table(asd, config)) | |
2291 | return -EINVAL; | |
2292 | } else { | |
2293 | /* Set ctc table to isp parameters */ | |
2294 | memcpy(&asd->params.css_param.ctc_table, config, | |
4995706d | 2295 | sizeof(asd->params.css_param.ctc_table)); |
a49d2536 AC |
2296 | atomisp_css_set_ctc_table(asd, &asd->params.css_param.ctc_table); |
2297 | } | |
2298 | ||
2299 | return 0; | |
2300 | } | |
2301 | ||
2302 | /* | |
2303 | * Function to update gamma correction parameters | |
2304 | */ | |
2305 | int atomisp_gamma_correction(struct atomisp_sub_device *asd, int flag, | |
2306 | struct atomisp_gc_config *config) | |
2307 | { | |
2308 | if (flag == 0) { | |
2309 | /* Get gamma correction params from current setup */ | |
2310 | if (atomisp_css_get_gc_config(asd, config)) | |
2311 | return -EINVAL; | |
2312 | } else { | |
2313 | /* Set gamma correction params to isp parameters */ | |
2314 | memcpy(&asd->params.css_param.gc_config, config, | |
4995706d | 2315 | sizeof(asd->params.css_param.gc_config)); |
a49d2536 AC |
2316 | atomisp_css_set_gc_config(asd, &asd->params.css_param.gc_config); |
2317 | asd->params.css_update_params_needed = true; | |
2318 | } | |
2319 | ||
2320 | return 0; | |
2321 | } | |
2322 | ||
2323 | /* | |
2324 | * Function to update narrow gamma flag | |
2325 | */ | |
2326 | int atomisp_formats(struct atomisp_sub_device *asd, int flag, | |
4995706d | 2327 | struct atomisp_formats_config *config) |
a49d2536 AC |
2328 | { |
2329 | if (flag == 0) { | |
2330 | /* Get narrow gamma flag from current setup */ | |
2331 | if (atomisp_css_get_formats_config(asd, config)) | |
2332 | return -EINVAL; | |
2333 | } else { | |
2334 | /* Set narrow gamma flag to isp parameters */ | |
2335 | memcpy(&asd->params.css_param.formats_config, config, | |
4995706d | 2336 | sizeof(asd->params.css_param.formats_config)); |
a49d2536 AC |
2337 | atomisp_css_set_formats_config(asd, &asd->params.css_param.formats_config); |
2338 | } | |
2339 | ||
2340 | return 0; | |
2341 | } | |
2342 | ||
2343 | void atomisp_free_internal_buffers(struct atomisp_sub_device *asd) | |
2344 | { | |
2345 | atomisp_free_css_parameters(&asd->params.css_param); | |
2346 | ||
2347 | if (asd->raw_output_frame) { | |
2348 | atomisp_css_frame_free(asd->raw_output_frame); | |
2349 | asd->raw_output_frame = NULL; | |
2350 | } | |
2351 | } | |
2352 | ||
2353 | static void atomisp_update_grid_info(struct atomisp_sub_device *asd, | |
4995706d DY |
2354 | enum atomisp_css_pipe_id pipe_id, |
2355 | int source_pad) | |
a49d2536 AC |
2356 | { |
2357 | struct atomisp_device *isp = asd->isp; | |
2358 | int err; | |
2359 | uint16_t stream_id = atomisp_source_pad_to_stream_id(asd, source_pad); | |
2360 | ||
2361 | if (atomisp_css_get_grid_info(asd, pipe_id, source_pad)) | |
2362 | return; | |
2363 | ||
2364 | /* We must free all buffers because they no longer match | |
2365 | the grid size. */ | |
2366 | atomisp_css_free_stat_buffers(asd); | |
2367 | ||
2368 | err = atomisp_alloc_css_stat_bufs(asd, stream_id); | |
2369 | if (err) { | |
2370 | dev_err(isp->dev, "stat_buf allocate error\n"); | |
2371 | goto err; | |
2372 | } | |
2373 | ||
2374 | if (atomisp_alloc_3a_output_buf(asd)) { | |
2375 | /* Failure for 3A buffers does not influence DIS buffers */ | |
2376 | if (asd->params.s3a_output_bytes != 0) { | |
2377 | /* For SOC sensor happens s3a_output_bytes == 0, | |
2378 | * using if condition to exclude false error log */ | |
2379 | dev_err(isp->dev, "Failed to allocate memory for 3A statistics\n"); | |
2380 | } | |
2381 | goto err; | |
2382 | } | |
2383 | ||
2384 | if (atomisp_alloc_dis_coef_buf(asd)) { | |
2385 | dev_err(isp->dev, | |
2386 | "Failed to allocate memory for DIS statistics\n"); | |
2387 | goto err; | |
2388 | } | |
2389 | ||
2390 | if (atomisp_alloc_metadata_output_buf(asd)) { | |
2391 | dev_err(isp->dev, "Failed to allocate memory for metadata\n"); | |
2392 | goto err; | |
2393 | } | |
2394 | ||
2395 | return; | |
2396 | ||
2397 | err: | |
2398 | atomisp_css_free_stat_buffers(asd); | |
2399 | return; | |
2400 | } | |
2401 | ||
2402 | static void atomisp_curr_user_grid_info(struct atomisp_sub_device *asd, | |
4995706d | 2403 | struct atomisp_grid_info *info) |
a49d2536 AC |
2404 | { |
2405 | memcpy(info, &asd->params.curr_grid_info.s3a_grid, | |
4995706d | 2406 | sizeof(struct atomisp_css_3a_grid_info)); |
a49d2536 AC |
2407 | } |
2408 | ||
2409 | int atomisp_compare_grid(struct atomisp_sub_device *asd, | |
2410 | struct atomisp_grid_info *atomgrid) | |
2411 | { | |
2412 | struct atomisp_grid_info tmp = {0}; | |
2413 | ||
2414 | atomisp_curr_user_grid_info(asd, &tmp); | |
2415 | return memcmp(atomgrid, &tmp, sizeof(tmp)); | |
2416 | } | |
2417 | ||
2418 | /* | |
2419 | * Function to update Gdc table for gdc | |
2420 | */ | |
2421 | int atomisp_gdc_cac_table(struct atomisp_sub_device *asd, int flag, | |
2422 | struct atomisp_morph_table *config) | |
2423 | { | |
2424 | int ret; | |
2425 | int i; | |
2426 | struct atomisp_device *isp = asd->isp; | |
2427 | ||
2428 | if (flag == 0) { | |
2429 | /* Get gdc table from current setup */ | |
2430 | struct atomisp_css_morph_table tab = {0}; | |
2431 | atomisp_css_get_morph_table(asd, &tab); | |
2432 | ||
2433 | config->width = tab.width; | |
2434 | config->height = tab.height; | |
2435 | ||
2436 | for (i = 0; i < CSS_MORPH_TABLE_NUM_PLANES; i++) { | |
2437 | ret = copy_to_user(config->coordinates_x[i], | |
2438 | tab.coordinates_x[i], tab.height * | |
2439 | tab.width * sizeof(*tab.coordinates_x[i])); | |
2440 | if (ret) { | |
2441 | dev_err(isp->dev, | |
2442 | "Failed to copy to User for x\n"); | |
2443 | return -EFAULT; | |
2444 | } | |
2445 | ret = copy_to_user(config->coordinates_y[i], | |
2446 | tab.coordinates_y[i], tab.height * | |
2447 | tab.width * sizeof(*tab.coordinates_y[i])); | |
2448 | if (ret) { | |
2449 | dev_err(isp->dev, | |
2450 | "Failed to copy to User for y\n"); | |
2451 | return -EFAULT; | |
2452 | } | |
2453 | } | |
2454 | } else { | |
2455 | struct atomisp_css_morph_table *tab = | |
2456 | asd->params.css_param.morph_table; | |
2457 | ||
2458 | /* free first if we have one */ | |
2459 | if (tab) { | |
2460 | atomisp_css_morph_table_free(tab); | |
2461 | asd->params.css_param.morph_table = NULL; | |
2462 | } | |
2463 | ||
2464 | /* allocate new one */ | |
2465 | tab = atomisp_css_morph_table_allocate(config->width, | |
4995706d | 2466 | config->height); |
a49d2536 AC |
2467 | |
2468 | if (!tab) { | |
2469 | dev_err(isp->dev, "out of memory\n"); | |
2470 | return -EINVAL; | |
2471 | } | |
2472 | ||
2473 | for (i = 0; i < CSS_MORPH_TABLE_NUM_PLANES; i++) { | |
2474 | ret = copy_from_user(tab->coordinates_x[i], | |
2475 | config->coordinates_x[i], | |
2476 | config->height * config->width * | |
2477 | sizeof(*config->coordinates_x[i])); | |
2478 | if (ret) { | |
2479 | dev_err(isp->dev, | |
2480 | "Failed to copy from User for x, ret %d\n", | |
2481 | ret); | |
2482 | atomisp_css_morph_table_free(tab); | |
2483 | return -EFAULT; | |
2484 | } | |
2485 | ret = copy_from_user(tab->coordinates_y[i], | |
2486 | config->coordinates_y[i], | |
2487 | config->height * config->width * | |
2488 | sizeof(*config->coordinates_y[i])); | |
2489 | if (ret) { | |
2490 | dev_err(isp->dev, | |
2491 | "Failed to copy from User for y, ret is %d\n", | |
2492 | ret); | |
2493 | atomisp_css_morph_table_free(tab); | |
2494 | return -EFAULT; | |
2495 | } | |
2496 | } | |
2497 | asd->params.css_param.morph_table = tab; | |
2498 | if (asd->params.gdc_cac_en) | |
2499 | atomisp_css_set_morph_table(asd, tab); | |
2500 | } | |
2501 | ||
2502 | return 0; | |
2503 | } | |
2504 | ||
2505 | int atomisp_macc_table(struct atomisp_sub_device *asd, int flag, | |
2506 | struct atomisp_macc_config *config) | |
2507 | { | |
2508 | struct atomisp_css_macc_table *macc_table; | |
2509 | ||
2510 | switch (config->color_effect) { | |
2511 | case V4L2_COLORFX_NONE: | |
2512 | macc_table = &asd->params.css_param.macc_table; | |
2513 | break; | |
2514 | case V4L2_COLORFX_SKY_BLUE: | |
2515 | macc_table = &blue_macc_table; | |
2516 | break; | |
2517 | case V4L2_COLORFX_GRASS_GREEN: | |
2518 | macc_table = &green_macc_table; | |
2519 | break; | |
2520 | case V4L2_COLORFX_SKIN_WHITEN_LOW: | |
2521 | macc_table = &skin_low_macc_table; | |
2522 | break; | |
2523 | case V4L2_COLORFX_SKIN_WHITEN: | |
2524 | macc_table = &skin_medium_macc_table; | |
2525 | break; | |
2526 | case V4L2_COLORFX_SKIN_WHITEN_HIGH: | |
2527 | macc_table = &skin_high_macc_table; | |
2528 | break; | |
2529 | default: | |
2530 | return -EINVAL; | |
2531 | } | |
2532 | ||
2533 | if (flag == 0) { | |
2534 | /* Get macc table from current setup */ | |
2535 | memcpy(&config->table, macc_table, | |
2536 | sizeof(struct atomisp_css_macc_table)); | |
2537 | } else { | |
2538 | memcpy(macc_table, &config->table, | |
2539 | sizeof(struct atomisp_css_macc_table)); | |
2540 | if (config->color_effect == asd->params.color_effect) | |
2541 | atomisp_css_set_macc_table(asd, macc_table); | |
2542 | } | |
2543 | ||
2544 | return 0; | |
2545 | } | |
2546 | ||
2547 | int atomisp_set_dis_vector(struct atomisp_sub_device *asd, | |
2548 | struct atomisp_dis_vector *vector) | |
2549 | { | |
2550 | atomisp_css_video_set_dis_vector(asd, vector); | |
2551 | ||
2552 | asd->params.dis_proj_data_valid = false; | |
2553 | asd->params.css_update_params_needed = true; | |
2554 | return 0; | |
2555 | } | |
2556 | ||
2557 | /* | |
2558 | * Function to set/get image stablization statistics | |
2559 | */ | |
2560 | int atomisp_get_dis_stat(struct atomisp_sub_device *asd, | |
2561 | struct atomisp_dis_statistics *stats) | |
2562 | { | |
2563 | return atomisp_css_get_dis_stat(asd, stats); | |
2564 | } | |
2565 | ||
2566 | /* | |
2567 | * Function set camrea_prefiles.xml current sensor pixel array size | |
2568 | */ | |
2569 | int atomisp_set_array_res(struct atomisp_sub_device *asd, | |
2570 | struct atomisp_resolution *config) | |
2571 | { | |
2572 | dev_dbg(asd->isp->dev, ">%s start\n", __func__); | |
1fe99664 | 2573 | if (!config) { |
a49d2536 AC |
2574 | dev_err(asd->isp->dev, "Set sensor array size is not valid\n"); |
2575 | return -EINVAL; | |
2576 | } | |
2577 | ||
2578 | asd->sensor_array_res.width = config->width; | |
2579 | asd->sensor_array_res.height = config->height; | |
2580 | return 0; | |
2581 | } | |
2582 | ||
2583 | /* | |
2584 | * Function to get DVS2 BQ resolution settings | |
2585 | */ | |
2586 | int atomisp_get_dvs2_bq_resolutions(struct atomisp_sub_device *asd, | |
2587 | struct atomisp_dvs2_bq_resolutions *bq_res) | |
2588 | { | |
2589 | struct ia_css_pipe_config *pipe_cfg = NULL; | |
2590 | struct ia_css_stream_config *stream_cfg = NULL; | |
2591 | struct ia_css_stream_input_config *input_config = NULL; | |
2592 | ||
2593 | struct ia_css_stream *stream = | |
2594 | asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream; | |
2595 | if (!stream) { | |
2596 | dev_warn(asd->isp->dev, "stream is not created"); | |
2597 | return -EAGAIN; | |
2598 | } | |
2599 | ||
2600 | pipe_cfg = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] | |
2601 | .pipe_configs[CSS_PIPE_ID_VIDEO]; | |
2602 | stream_cfg = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] | |
2603 | .stream_config; | |
2604 | input_config = &stream_cfg->input_config; | |
2605 | ||
2606 | if (!bq_res) | |
2607 | return -EINVAL; | |
2608 | ||
2609 | /* the GDC output resolution */ | |
2610 | bq_res->output_bq.width_bq = pipe_cfg->output_info[0].res.width / 2; | |
2611 | bq_res->output_bq.height_bq = pipe_cfg->output_info[0].res.height / 2; | |
2612 | ||
2613 | bq_res->envelope_bq.width_bq = 0; | |
2614 | bq_res->envelope_bq.height_bq = 0; | |
2615 | /* the GDC input resolution */ | |
2616 | if (!asd->continuous_mode->val) { | |
2617 | bq_res->source_bq.width_bq = bq_res->output_bq.width_bq + | |
2618 | pipe_cfg->dvs_envelope.width / 2; | |
2619 | bq_res->source_bq.height_bq = bq_res->output_bq.height_bq + | |
2620 | pipe_cfg->dvs_envelope.height / 2; | |
2621 | /* | |
2622 | * Bad pixels caused by spatial filter processing | |
2623 | * ISP filter resolution should be given by CSS/FW, but for now | |
2624 | * there is not such API to query, and it is fixed value, so | |
2625 | * hardcoded here. | |
2626 | */ | |
2627 | bq_res->ispfilter_bq.width_bq = 12 / 2; | |
2628 | bq_res->ispfilter_bq.height_bq = 12 / 2; | |
2629 | /* spatial filter shift, always 4 pixels */ | |
2630 | bq_res->gdc_shift_bq.width_bq = 4 / 2; | |
2631 | bq_res->gdc_shift_bq.height_bq = 4 / 2; | |
2632 | ||
2633 | if (asd->params.video_dis_en) { | |
2634 | bq_res->envelope_bq.width_bq = pipe_cfg->dvs_envelope.width | |
2635 | / 2 - bq_res->ispfilter_bq.width_bq; | |
2636 | bq_res->envelope_bq.height_bq = pipe_cfg->dvs_envelope.height | |
2637 | / 2 - bq_res->ispfilter_bq.height_bq; | |
2638 | } | |
2639 | } else { | |
2640 | unsigned int w_padding; | |
2641 | unsigned int gdc_effective_input = 0; | |
2642 | ||
2643 | /* For GDC: | |
2644 | * gdc_effective_input = effective_input + envelope | |
2645 | * | |
2646 | * From the comment and formula in BZ1786, | |
2647 | * we see the source_bq should be: | |
2648 | * effective_input / bayer_ds_ratio | |
2649 | */ | |
2650 | bq_res->source_bq.width_bq = | |
2651 | (input_config->effective_res.width * | |
2652 | pipe_cfg->bayer_ds_out_res.width / | |
2653 | input_config->effective_res.width + 1) / 2; | |
2654 | bq_res->source_bq.height_bq = | |
2655 | (input_config->effective_res.height * | |
2656 | pipe_cfg->bayer_ds_out_res.height / | |
2657 | input_config->effective_res.height + 1) / 2; | |
2658 | ||
2659 | ||
2660 | if (!asd->params.video_dis_en) { | |
2661 | /* | |
2662 | * We adjust the ispfilter_bq to: | |
2663 | * ispfilter_bq = 128/BDS | |
2664 | * we still need firmware team to provide an offical | |
2665 | * formula for SDV. | |
2666 | */ | |
2667 | bq_res->ispfilter_bq.width_bq = 128 * | |
2668 | pipe_cfg->bayer_ds_out_res.width / | |
2669 | input_config->effective_res.width / 2; | |
2670 | bq_res->ispfilter_bq.height_bq = 128 * | |
2671 | pipe_cfg->bayer_ds_out_res.width / | |
2672 | input_config->effective_res.width / 2; | |
2673 | ||
2674 | if (IS_HWREVISION(asd->isp, ATOMISP_HW_REVISION_ISP2401)) { | |
2675 | /* No additional left padding for ISYS2401 */ | |
2676 | bq_res->gdc_shift_bq.width_bq = 4 / 2; | |
2677 | bq_res->gdc_shift_bq.height_bq = 4 / 2; | |
2678 | } else { | |
2679 | /* | |
2680 | * For the w_padding and gdc_shift_bq cacluation | |
2681 | * Please see the BZ 1786 and 4358 for more info. | |
2682 | * Just test that this formula can work now, | |
2683 | * but we still have no offical formula. | |
2684 | * | |
2685 | * w_padding = ceiling(gdc_effective_input | |
2686 | * /128, 1) * 128 - effective_width | |
2687 | * gdc_shift_bq = w_padding/BDS/2 + ispfilter_bq/2 | |
2688 | */ | |
2689 | gdc_effective_input = | |
2690 | input_config->effective_res.width + | |
2691 | pipe_cfg->dvs_envelope.width; | |
2692 | w_padding = roundup(gdc_effective_input, 128) - | |
2693 | input_config->effective_res.width; | |
2694 | w_padding = w_padding * | |
2695 | pipe_cfg->bayer_ds_out_res.width / | |
2696 | input_config->effective_res.width + 1; | |
2697 | w_padding = roundup(w_padding/2, 1); | |
2698 | ||
2699 | bq_res->gdc_shift_bq.width_bq = bq_res->ispfilter_bq.width_bq / 2 | |
2700 | + w_padding; | |
2701 | bq_res->gdc_shift_bq.height_bq = 4 / 2; | |
2702 | } | |
2703 | } else { | |
2704 | unsigned int dvs_w, dvs_h, dvs_w_max, dvs_h_max; | |
2705 | ||
2706 | bq_res->ispfilter_bq.width_bq = 8 / 2; | |
2707 | bq_res->ispfilter_bq.height_bq = 8 / 2; | |
2708 | ||
2709 | if (IS_HWREVISION(asd->isp, ATOMISP_HW_REVISION_ISP2401)) { | |
2710 | /* No additional left padding for ISYS2401 */ | |
2711 | bq_res->gdc_shift_bq.width_bq = 4 / 2; | |
2712 | bq_res->gdc_shift_bq.height_bq = 4 / 2; | |
2713 | } else { | |
2714 | w_padding = | |
2715 | roundup(input_config->effective_res.width, 128) - | |
2716 | input_config->effective_res.width; | |
2717 | if (w_padding < 12) | |
2718 | w_padding = 12; | |
2719 | bq_res->gdc_shift_bq.width_bq = 4 / 2 + | |
2720 | ((w_padding - 12) * | |
2721 | pipe_cfg->bayer_ds_out_res.width / | |
2722 | input_config->effective_res.width + 1) / 2; | |
2723 | bq_res->gdc_shift_bq.height_bq = 4 / 2; | |
2724 | } | |
2725 | ||
2726 | dvs_w = pipe_cfg->bayer_ds_out_res.width - | |
2727 | pipe_cfg->output_info[0].res.width; | |
2728 | dvs_h = pipe_cfg->bayer_ds_out_res.height - | |
2729 | pipe_cfg->output_info[0].res.height; | |
2730 | dvs_w_max = rounddown( | |
2731 | pipe_cfg->output_info[0].res.width / 5, | |
2732 | ATOM_ISP_STEP_WIDTH); | |
2733 | dvs_h_max = rounddown( | |
2734 | pipe_cfg->output_info[0].res.height / 5, | |
2735 | ATOM_ISP_STEP_HEIGHT); | |
2736 | bq_res->envelope_bq.width_bq = | |
2737 | min((dvs_w / 2), (dvs_w_max / 2)) - | |
2738 | bq_res->ispfilter_bq.width_bq; | |
2739 | bq_res->envelope_bq.height_bq = | |
2740 | min((dvs_h / 2), (dvs_h_max / 2)) - | |
2741 | bq_res->ispfilter_bq.height_bq; | |
2742 | } | |
2743 | } | |
2744 | ||
2745 | dev_dbg(asd->isp->dev, "source_bq.width_bq %d, source_bq.height_bq %d,\nispfilter_bq.width_bq %d, ispfilter_bq.height_bq %d,\ngdc_shift_bq.width_bq %d, gdc_shift_bq.height_bq %d,\nenvelope_bq.width_bq %d, envelope_bq.height_bq %d,\noutput_bq.width_bq %d, output_bq.height_bq %d\n", | |
2746 | bq_res->source_bq.width_bq, bq_res->source_bq.height_bq, | |
2747 | bq_res->ispfilter_bq.width_bq, bq_res->ispfilter_bq.height_bq, | |
2748 | bq_res->gdc_shift_bq.width_bq, bq_res->gdc_shift_bq.height_bq, | |
2749 | bq_res->envelope_bq.width_bq, bq_res->envelope_bq.height_bq, | |
2750 | bq_res->output_bq.width_bq, bq_res->output_bq.height_bq); | |
2751 | ||
2752 | return 0; | |
2753 | } | |
2754 | ||
2755 | int atomisp_set_dis_coefs(struct atomisp_sub_device *asd, | |
2756 | struct atomisp_dis_coefficients *coefs) | |
2757 | { | |
2758 | return atomisp_css_set_dis_coefs(asd, coefs); | |
2759 | } | |
2760 | ||
2761 | /* | |
2762 | * Function to set/get 3A stat from isp | |
2763 | */ | |
2764 | int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, | |
2765 | struct atomisp_3a_statistics *config) | |
2766 | { | |
2767 | struct atomisp_device *isp = asd->isp; | |
2768 | struct atomisp_s3a_buf *s3a_buf; | |
2769 | unsigned long ret; | |
2770 | ||
2771 | if (flag != 0) | |
2772 | return -EINVAL; | |
2773 | ||
2774 | /* sanity check to avoid writing into unallocated memory. */ | |
2775 | if (asd->params.s3a_output_bytes == 0) | |
2776 | return -EINVAL; | |
2777 | ||
2778 | if (atomisp_compare_grid(asd, &config->grid_info) != 0) { | |
2779 | /* If the grid info in the argument differs from the current | |
2780 | grid info, we tell the caller to reset the grid size and | |
2781 | try again. */ | |
2782 | return -EAGAIN; | |
2783 | } | |
2784 | ||
2785 | if (list_empty(&asd->s3a_stats_ready)) { | |
2786 | dev_err(isp->dev, "3a statistics is not valid.\n"); | |
2787 | return -EAGAIN; | |
2788 | } | |
2789 | ||
2790 | s3a_buf = list_entry(asd->s3a_stats_ready.next, | |
2791 | struct atomisp_s3a_buf, list); | |
2792 | if (s3a_buf->s3a_map) | |
2793 | ia_css_translate_3a_statistics( | |
2794 | asd->params.s3a_user_stat, s3a_buf->s3a_map); | |
2795 | else | |
2796 | ia_css_get_3a_statistics(asd->params.s3a_user_stat, | |
2797 | s3a_buf->s3a_data); | |
2798 | ||
2799 | config->exp_id = s3a_buf->s3a_data->exp_id; | |
2800 | config->isp_config_id = s3a_buf->s3a_data->isp_config_id; | |
2801 | ||
2802 | ret = copy_to_user(config->data, asd->params.s3a_user_stat->data, | |
2803 | asd->params.s3a_output_bytes); | |
2804 | if (ret) { | |
2805 | dev_err(isp->dev, "copy to user failed: copied %lu bytes\n", | |
2806 | ret); | |
2807 | return -EFAULT; | |
2808 | } | |
2809 | ||
2810 | /* Move to free buffer list */ | |
2811 | list_del_init(&s3a_buf->list); | |
2812 | list_add_tail(&s3a_buf->list, &asd->s3a_stats); | |
2813 | dev_dbg(isp->dev, "%s: finish getting exp_id %d 3a stat, isp_config_id %d\n", __func__, | |
2814 | config->exp_id, config->isp_config_id); | |
2815 | return 0; | |
2816 | } | |
2817 | ||
2818 | int atomisp_get_metadata(struct atomisp_sub_device *asd, int flag, | |
2819 | struct atomisp_metadata *md) | |
2820 | { | |
2821 | struct atomisp_device *isp = asd->isp; | |
2822 | struct ia_css_stream_config *stream_config; | |
2823 | struct ia_css_stream_info *stream_info; | |
2824 | struct camera_mipi_info *mipi_info; | |
2825 | struct atomisp_metadata_buf *md_buf; | |
2826 | enum atomisp_metadata_type md_type = ATOMISP_MAIN_METADATA; | |
2827 | int ret, i; | |
2828 | ||
2829 | if (flag != 0) | |
2830 | return -EINVAL; | |
2831 | ||
2832 | stream_config = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. | |
2833 | stream_config; | |
2834 | stream_info = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. | |
2835 | stream_info; | |
2836 | ||
2837 | /* We always return the resolution and stride even if there is | |
2838 | * no valid metadata. This allows the caller to get the information | |
2839 | * needed to allocate user-space buffers. */ | |
2840 | md->width = stream_info->metadata_info.resolution.width; | |
2841 | md->height = stream_info->metadata_info.resolution.height; | |
2842 | md->stride = stream_info->metadata_info.stride; | |
2843 | ||
2844 | /* sanity check to avoid writing into unallocated memory. | |
2845 | * This does not return an error because it is a valid way | |
2846 | * for applications to detect that metadata is not enabled. */ | |
2847 | if (md->width == 0 || md->height == 0 || !md->data) | |
2848 | return 0; | |
2849 | ||
2850 | /* This is done in the atomisp_buf_done() */ | |
2851 | if (list_empty(&asd->metadata_ready[md_type])) { | |
2852 | dev_warn(isp->dev, "Metadata queue is empty now!\n"); | |
2853 | return -EAGAIN; | |
2854 | } | |
2855 | ||
2856 | mipi_info = atomisp_to_sensor_mipi_info( | |
2857 | isp->inputs[asd->input_curr].camera); | |
2858 | if (mipi_info == NULL) | |
2859 | return -EINVAL; | |
2860 | ||
2861 | if (mipi_info->metadata_effective_width != NULL) { | |
2862 | for (i = 0; i < md->height; i++) | |
2863 | md->effective_width[i] = | |
2864 | mipi_info->metadata_effective_width[i]; | |
2865 | } | |
2866 | ||
2867 | md_buf = list_entry(asd->metadata_ready[md_type].next, | |
4995706d | 2868 | struct atomisp_metadata_buf, list); |
a49d2536 AC |
2869 | md->exp_id = md_buf->metadata->exp_id; |
2870 | if (md_buf->md_vptr) { | |
2871 | ret = copy_to_user(md->data, | |
4995706d DY |
2872 | md_buf->md_vptr, |
2873 | stream_info->metadata_info.size); | |
a49d2536 | 2874 | } else { |
08674e98 | 2875 | hmm_load(md_buf->metadata->address, |
4995706d DY |
2876 | asd->params.metadata_user[md_type], |
2877 | stream_info->metadata_info.size); | |
a49d2536 AC |
2878 | |
2879 | ret = copy_to_user(md->data, | |
4995706d DY |
2880 | asd->params.metadata_user[md_type], |
2881 | stream_info->metadata_info.size); | |
a49d2536 AC |
2882 | } |
2883 | if (ret) { | |
2884 | dev_err(isp->dev, "copy to user failed: copied %d bytes\n", | |
2885 | ret); | |
2886 | return -EFAULT; | |
a49d2536 AC |
2887 | } |
2888 | ||
e9f11ace DY |
2889 | list_del_init(&md_buf->list); |
2890 | list_add_tail(&md_buf->list, &asd->metadata[md_type]); | |
2891 | ||
a49d2536 AC |
2892 | dev_dbg(isp->dev, "%s: HAL de-queued metadata type %d with exp_id %d\n", |
2893 | __func__, md_type, md->exp_id); | |
2894 | return 0; | |
2895 | } | |
2896 | ||
2897 | int atomisp_get_metadata_by_type(struct atomisp_sub_device *asd, int flag, | |
2898 | struct atomisp_metadata_with_type *md) | |
2899 | { | |
2900 | struct atomisp_device *isp = asd->isp; | |
2901 | struct ia_css_stream_config *stream_config; | |
2902 | struct ia_css_stream_info *stream_info; | |
2903 | struct camera_mipi_info *mipi_info; | |
2904 | struct atomisp_metadata_buf *md_buf; | |
2905 | enum atomisp_metadata_type md_type; | |
2906 | int ret, i; | |
2907 | ||
2908 | if (flag != 0) | |
2909 | return -EINVAL; | |
2910 | ||
2911 | stream_config = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. | |
2912 | stream_config; | |
2913 | stream_info = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. | |
2914 | stream_info; | |
2915 | ||
2916 | /* We always return the resolution and stride even if there is | |
2917 | * no valid metadata. This allows the caller to get the information | |
2918 | * needed to allocate user-space buffers. */ | |
2919 | md->width = stream_info->metadata_info.resolution.width; | |
2920 | md->height = stream_info->metadata_info.resolution.height; | |
2921 | md->stride = stream_info->metadata_info.stride; | |
2922 | ||
2923 | /* sanity check to avoid writing into unallocated memory. | |
2924 | * This does not return an error because it is a valid way | |
2925 | * for applications to detect that metadata is not enabled. */ | |
2926 | if (md->width == 0 || md->height == 0 || !md->data) | |
2927 | return 0; | |
2928 | ||
2929 | md_type = md->type; | |
39c116dc | 2930 | if (md_type < 0 || md_type >= ATOMISP_METADATA_TYPE_NUM) |
a49d2536 AC |
2931 | return -EINVAL; |
2932 | ||
2933 | /* This is done in the atomisp_buf_done() */ | |
2934 | if (list_empty(&asd->metadata_ready[md_type])) { | |
2935 | dev_warn(isp->dev, "Metadata queue is empty now!\n"); | |
2936 | return -EAGAIN; | |
2937 | } | |
2938 | ||
2939 | mipi_info = atomisp_to_sensor_mipi_info( | |
2940 | isp->inputs[asd->input_curr].camera); | |
2941 | if (mipi_info == NULL) | |
2942 | return -EINVAL; | |
2943 | ||
2944 | if (mipi_info->metadata_effective_width != NULL) { | |
2945 | for (i = 0; i < md->height; i++) | |
2946 | md->effective_width[i] = | |
2947 | mipi_info->metadata_effective_width[i]; | |
2948 | } | |
2949 | ||
2950 | md_buf = list_entry(asd->metadata_ready[md_type].next, | |
4995706d | 2951 | struct atomisp_metadata_buf, list); |
a49d2536 AC |
2952 | md->exp_id = md_buf->metadata->exp_id; |
2953 | if (md_buf->md_vptr) { | |
2954 | ret = copy_to_user(md->data, | |
4995706d DY |
2955 | md_buf->md_vptr, |
2956 | stream_info->metadata_info.size); | |
a49d2536 | 2957 | } else { |
08674e98 | 2958 | hmm_load(md_buf->metadata->address, |
4995706d DY |
2959 | asd->params.metadata_user[md_type], |
2960 | stream_info->metadata_info.size); | |
a49d2536 AC |
2961 | |
2962 | ret = copy_to_user(md->data, | |
4995706d DY |
2963 | asd->params.metadata_user[md_type], |
2964 | stream_info->metadata_info.size); | |
a49d2536 AC |
2965 | } |
2966 | if (ret) { | |
2967 | dev_err(isp->dev, "copy to user failed: copied %d bytes\n", | |
2968 | ret); | |
2969 | return -EFAULT; | |
2970 | } else { | |
2971 | list_del_init(&md_buf->list); | |
2972 | list_add_tail(&md_buf->list, &asd->metadata[md_type]); | |
2973 | } | |
2974 | dev_dbg(isp->dev, "%s: HAL de-queued metadata type %d with exp_id %d\n", | |
2975 | __func__, md_type, md->exp_id); | |
2976 | return 0; | |
2977 | } | |
2978 | ||
2979 | /* | |
2980 | * Function to calculate real zoom region for every pipe | |
2981 | */ | |
2982 | int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, | |
4995706d DY |
2983 | struct ia_css_dz_config *dz_config, |
2984 | enum atomisp_css_pipe_id css_pipe_id) | |
a49d2536 AC |
2985 | |
2986 | { | |
2987 | struct atomisp_stream_env *stream_env = | |
2988 | &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; | |
2989 | struct atomisp_resolution eff_res, out_res; | |
2990 | #ifdef ISP2401 | |
2991 | int w_offset, h_offset; | |
2992 | #endif | |
2993 | ||
2994 | memset(&eff_res, 0, sizeof(eff_res)); | |
2995 | memset(&out_res, 0, sizeof(out_res)); | |
2996 | ||
2997 | if (dz_config->dx || dz_config->dy) | |
2998 | return 0; | |
2999 | ||
3000 | if (css_pipe_id != IA_CSS_PIPE_ID_PREVIEW | |
3001 | && css_pipe_id != IA_CSS_PIPE_ID_CAPTURE) { | |
3002 | dev_err(asd->isp->dev, "%s the set pipe no support crop region" | |
3003 | , __func__); | |
3004 | return -EINVAL; | |
3005 | } | |
3006 | ||
3007 | eff_res.width = | |
3008 | stream_env->stream_config.input_config.effective_res.width; | |
3009 | eff_res.height = | |
3010 | stream_env->stream_config.input_config.effective_res.height; | |
3011 | if (eff_res.width == 0 || eff_res.height == 0) { | |
3012 | dev_err(asd->isp->dev, "%s err effective resolution" | |
3013 | , __func__); | |
3014 | return -EINVAL; | |
3015 | } | |
3016 | ||
3017 | if (dz_config->zoom_region.resolution.width | |
3018 | == asd->sensor_array_res.width | |
3019 | || dz_config->zoom_region.resolution.height | |
3020 | == asd->sensor_array_res.height) { | |
3021 | /*no need crop region*/ | |
3022 | dz_config->zoom_region.origin.x = 0; | |
3023 | dz_config->zoom_region.origin.y = 0; | |
3024 | dz_config->zoom_region.resolution.width = eff_res.width; | |
3025 | dz_config->zoom_region.resolution.height = eff_res.height; | |
3026 | return 0; | |
3027 | } | |
3028 | ||
3029 | /* FIXME: | |
3030 | * This is not the correct implementation with Google's definition, due | |
3031 | * to firmware limitation. | |
3032 | * map real crop region base on above calculating base max crop region. | |
3033 | */ | |
3034 | #ifdef ISP2401 | |
3035 | out_res.width = | |
3036 | stream_env->pipe_configs[css_pipe_id].output_info[0].res.width; | |
3037 | out_res.height = | |
3038 | stream_env->pipe_configs[css_pipe_id].output_info[0].res.height; | |
3039 | if (out_res.width == 0 || out_res.height == 0) { | |
3040 | dev_err(asd->isp->dev, "%s err current pipe output resolution" | |
3041 | , __func__); | |
3042 | return -EINVAL; | |
3043 | } | |
3044 | ||
3045 | if (asd->sensor_array_res.width * out_res.height | |
3046 | < out_res.width * asd->sensor_array_res.height) { | |
3047 | h_offset = asd->sensor_array_res.height - | |
3048 | asd->sensor_array_res.width | |
3049 | * out_res.height / out_res.width; | |
3050 | h_offset = h_offset / 2; | |
3051 | if (dz_config->zoom_region.origin.y < h_offset) | |
3052 | dz_config->zoom_region.origin.y = 0; | |
3053 | else | |
3054 | dz_config->zoom_region.origin.y = | |
3055 | dz_config->zoom_region.origin.y - h_offset; | |
3056 | w_offset = 0; | |
3057 | } else { | |
3058 | w_offset = asd->sensor_array_res.width - | |
3059 | asd->sensor_array_res.height | |
3060 | * out_res.width / out_res.height; | |
3061 | w_offset = w_offset / 2; | |
3062 | if (dz_config->zoom_region.origin.x < w_offset) | |
3063 | dz_config->zoom_region.origin.x = 0; | |
3064 | else | |
3065 | dz_config->zoom_region.origin.x = | |
3066 | dz_config->zoom_region.origin.x - w_offset; | |
3067 | h_offset = 0; | |
3068 | } | |
3069 | #endif | |
3070 | dz_config->zoom_region.origin.x = | |
3071 | dz_config->zoom_region.origin.x | |
3072 | * eff_res.width | |
3073 | #ifndef ISP2401 | |
3074 | / asd->sensor_array_res.width; | |
3075 | #else | |
3076 | / (asd->sensor_array_res.width - | |
3077 | 2 * w_offset); | |
3078 | #endif | |
3079 | dz_config->zoom_region.origin.y = | |
3080 | dz_config->zoom_region.origin.y | |
3081 | * eff_res.height | |
3082 | #ifndef ISP2401 | |
3083 | / asd->sensor_array_res.height; | |
3084 | #else | |
3085 | / (asd->sensor_array_res.height - | |
3086 | 2 * h_offset); | |
3087 | #endif | |
3088 | dz_config->zoom_region.resolution.width = | |
3089 | dz_config->zoom_region.resolution.width | |
3090 | * eff_res.width | |
3091 | #ifndef ISP2401 | |
3092 | / asd->sensor_array_res.width; | |
3093 | #else | |
3094 | / (asd->sensor_array_res.width - | |
3095 | 2 * w_offset); | |
3096 | #endif | |
3097 | dz_config->zoom_region.resolution.height = | |
3098 | dz_config->zoom_region.resolution.height | |
3099 | * eff_res.height | |
3100 | #ifndef ISP2401 | |
3101 | / asd->sensor_array_res.height; | |
3102 | #else | |
3103 | / (asd->sensor_array_res.height - | |
3104 | 2 * h_offset); | |
3105 | #endif | |
3106 | ||
3107 | /* | |
3108 | * Set same ratio of crop region resolution and current pipe output | |
3109 | * resolution | |
3110 | */ | |
3111 | #ifndef ISP2401 | |
3112 | out_res.width = | |
3113 | stream_env->pipe_configs[css_pipe_id].output_info[0].res.width; | |
3114 | out_res.height = | |
3115 | stream_env->pipe_configs[css_pipe_id].output_info[0].res.height; | |
3116 | if (out_res.width == 0 || out_res.height == 0) { | |
3117 | dev_err(asd->isp->dev, "%s err current pipe output resolution" | |
3118 | , __func__); | |
3119 | return -EINVAL; | |
3120 | } | |
3121 | ||
3122 | #endif | |
3123 | if (out_res.width * dz_config->zoom_region.resolution.height | |
3124 | > dz_config->zoom_region.resolution.width * out_res.height) { | |
3125 | dz_config->zoom_region.resolution.height = | |
3126 | dz_config->zoom_region.resolution.width | |
3127 | * out_res.height / out_res.width; | |
3128 | } else { | |
3129 | dz_config->zoom_region.resolution.width = | |
3130 | dz_config->zoom_region.resolution.height | |
3131 | * out_res.width / out_res.height; | |
3132 | } | |
3133 | dev_dbg(asd->isp->dev, "%s crop region:(%d,%d),(%d,%d) eff_res(%d, %d) array_size(%d,%d) out_res(%d, %d)\n", | |
3134 | __func__, dz_config->zoom_region.origin.x, | |
3135 | dz_config->zoom_region.origin.y, | |
3136 | dz_config->zoom_region.resolution.width, | |
3137 | dz_config->zoom_region.resolution.height, | |
3138 | eff_res.width, eff_res.height, | |
3139 | asd->sensor_array_res.width, | |
3140 | asd->sensor_array_res.height, | |
3141 | out_res.width, out_res.height); | |
3142 | ||
3143 | ||
3144 | if ((dz_config->zoom_region.origin.x + | |
3145 | dz_config->zoom_region.resolution.width | |
3146 | > eff_res.width) || | |
3147 | (dz_config->zoom_region.origin.y + | |
3148 | dz_config->zoom_region.resolution.height | |
3149 | > eff_res.height)) | |
3150 | return -EINVAL; | |
3151 | ||
3152 | return 0; | |
3153 | } | |
3154 | ||
3155 | ||
3156 | /* | |
3157 | * Function to check the zoom region whether is effective | |
3158 | */ | |
3159 | static bool atomisp_check_zoom_region( | |
3160 | struct atomisp_sub_device *asd, | |
3161 | struct ia_css_dz_config *dz_config) | |
3162 | { | |
3163 | struct atomisp_resolution config; | |
3164 | bool flag = false; | |
3165 | unsigned int w , h; | |
3166 | ||
3167 | memset(&config, 0, sizeof(struct atomisp_resolution)); | |
3168 | ||
3169 | if (dz_config->dx && dz_config->dy) | |
3170 | return true; | |
3171 | ||
3172 | config.width = asd->sensor_array_res.width; | |
3173 | config.height = asd->sensor_array_res.height; | |
3174 | w = dz_config->zoom_region.origin.x + | |
3175 | dz_config->zoom_region.resolution.width; | |
3176 | h = dz_config->zoom_region.origin.y + | |
3177 | dz_config->zoom_region.resolution.height; | |
3178 | ||
3179 | if ((w <= config.width) && (h <= config.height) && w > 0 && h > 0) | |
3180 | flag = true; | |
3181 | else | |
3182 | /* setting error zoom region */ | |
3183 | dev_err(asd->isp->dev, "%s zoom region ERROR:dz_config:(%d,%d),(%d,%d)array_res(%d, %d)\n", | |
4995706d DY |
3184 | __func__, dz_config->zoom_region.origin.x, |
3185 | dz_config->zoom_region.origin.y, | |
3186 | dz_config->zoom_region.resolution.width, | |
3187 | dz_config->zoom_region.resolution.height, | |
3188 | config.width, config.height); | |
a49d2536 AC |
3189 | |
3190 | return flag; | |
3191 | } | |
3192 | ||
3193 | void atomisp_apply_css_parameters( | |
3194 | struct atomisp_sub_device *asd, | |
3195 | struct atomisp_css_params *css_param) | |
3196 | { | |
3197 | if (css_param->update_flag.wb_config) | |
3198 | atomisp_css_set_wb_config(asd, &css_param->wb_config); | |
3199 | ||
3200 | if (css_param->update_flag.ob_config) | |
3201 | atomisp_css_set_ob_config(asd, &css_param->ob_config); | |
3202 | ||
3203 | if (css_param->update_flag.dp_config) | |
3204 | atomisp_css_set_dp_config(asd, &css_param->dp_config); | |
3205 | ||
3206 | if (css_param->update_flag.nr_config) | |
3207 | atomisp_css_set_nr_config(asd, &css_param->nr_config); | |
3208 | ||
3209 | if (css_param->update_flag.ee_config) | |
3210 | atomisp_css_set_ee_config(asd, &css_param->ee_config); | |
3211 | ||
3212 | if (css_param->update_flag.tnr_config) | |
3213 | atomisp_css_set_tnr_config(asd, &css_param->tnr_config); | |
3214 | ||
3215 | if (css_param->update_flag.a3a_config) | |
3216 | atomisp_css_set_3a_config(asd, &css_param->s3a_config); | |
3217 | ||
3218 | if (css_param->update_flag.ctc_config) | |
3219 | atomisp_css_set_ctc_config(asd, &css_param->ctc_config); | |
3220 | ||
3221 | if (css_param->update_flag.cnr_config) | |
3222 | atomisp_css_set_cnr_config(asd, &css_param->cnr_config); | |
3223 | ||
3224 | if (css_param->update_flag.ecd_config) | |
3225 | atomisp_css_set_ecd_config(asd, &css_param->ecd_config); | |
3226 | ||
3227 | if (css_param->update_flag.ynr_config) | |
3228 | atomisp_css_set_ynr_config(asd, &css_param->ynr_config); | |
3229 | ||
3230 | if (css_param->update_flag.fc_config) | |
3231 | atomisp_css_set_fc_config(asd, &css_param->fc_config); | |
3232 | ||
3233 | if (css_param->update_flag.macc_config) | |
3234 | atomisp_css_set_macc_config(asd, &css_param->macc_config); | |
3235 | ||
3236 | if (css_param->update_flag.aa_config) | |
3237 | atomisp_css_set_aa_config(asd, &css_param->aa_config); | |
3238 | ||
3239 | if (css_param->update_flag.anr_config) | |
3240 | atomisp_css_set_anr_config(asd, &css_param->anr_config); | |
3241 | ||
3242 | if (css_param->update_flag.xnr_config) | |
3243 | atomisp_css_set_xnr_config(asd, &css_param->xnr_config); | |
3244 | ||
3245 | if (css_param->update_flag.yuv2rgb_cc_config) | |
3246 | atomisp_css_set_yuv2rgb_cc_config(asd, | |
3247 | &css_param->yuv2rgb_cc_config); | |
3248 | ||
3249 | if (css_param->update_flag.rgb2yuv_cc_config) | |
3250 | atomisp_css_set_rgb2yuv_cc_config(asd, | |
3251 | &css_param->rgb2yuv_cc_config); | |
3252 | ||
3253 | if (css_param->update_flag.macc_table) | |
3254 | atomisp_css_set_macc_table(asd, &css_param->macc_table); | |
3255 | ||
3256 | if (css_param->update_flag.xnr_table) | |
3257 | atomisp_css_set_xnr_table(asd, &css_param->xnr_table); | |
3258 | ||
3259 | if (css_param->update_flag.r_gamma_table) | |
3260 | atomisp_css_set_r_gamma_table(asd, &css_param->r_gamma_table); | |
3261 | ||
3262 | if (css_param->update_flag.g_gamma_table) | |
3263 | atomisp_css_set_g_gamma_table(asd, &css_param->g_gamma_table); | |
3264 | ||
3265 | if (css_param->update_flag.b_gamma_table) | |
3266 | atomisp_css_set_b_gamma_table(asd, &css_param->b_gamma_table); | |
3267 | ||
3268 | if (css_param->update_flag.anr_thres) | |
3269 | atomisp_css_set_anr_thres(asd, &css_param->anr_thres); | |
3270 | ||
3271 | if (css_param->update_flag.shading_table) | |
3272 | atomisp_css_set_shading_table(asd, css_param->shading_table); | |
3273 | ||
3274 | if (css_param->update_flag.morph_table && asd->params.gdc_cac_en) | |
3275 | atomisp_css_set_morph_table(asd, css_param->morph_table); | |
3276 | ||
3277 | if (css_param->update_flag.dvs2_coefs) { | |
3278 | struct atomisp_css_dvs_grid_info *dvs_grid_info = | |
3279 | atomisp_css_get_dvs_grid_info( | |
3280 | &asd->params.curr_grid_info); | |
3281 | ||
3282 | if (dvs_grid_info && dvs_grid_info->enable) | |
3283 | atomisp_css_set_dvs2_coefs(asd, css_param->dvs2_coeff); | |
3284 | } | |
3285 | ||
3286 | if (css_param->update_flag.dvs_6axis_config) | |
3287 | atomisp_css_set_dvs_6axis(asd, css_param->dvs_6axis); | |
3288 | ||
3289 | atomisp_css_set_isp_config_id(asd, css_param->isp_config_id); | |
3290 | /* | |
3291 | * These configurations are on used by ISP1.x, not for ISP2.x, | |
3292 | * so do not handle them. see comments of ia_css_isp_config. | |
3293 | * 1 cc_config | |
3294 | * 2 ce_config | |
3295 | * 3 de_config | |
3296 | * 4 gc_config | |
3297 | * 5 gamma_table | |
3298 | * 6 ctc_table | |
3299 | * 7 dvs_coefs | |
3300 | */ | |
3301 | } | |
3302 | ||
3303 | static unsigned int long copy_from_compatible(void *to, const void *from, | |
4995706d | 3304 | unsigned long n, bool from_user) |
a49d2536 AC |
3305 | { |
3306 | if (from_user) | |
3307 | return copy_from_user(to, from, n); | |
3308 | else | |
3309 | memcpy(to, from, n); | |
3310 | return 0; | |
3311 | } | |
3312 | ||
3313 | int atomisp_cp_general_isp_parameters(struct atomisp_sub_device *asd, | |
3314 | struct atomisp_parameters *arg, | |
3315 | struct atomisp_css_params *css_param, | |
3316 | bool from_user) | |
3317 | { | |
3318 | struct atomisp_parameters *cur_config = &css_param->update_flag; | |
3319 | ||
3320 | if (!arg || !asd || !css_param) | |
3321 | return -EINVAL; | |
3322 | ||
3323 | if (arg->wb_config && (from_user || !cur_config->wb_config)) { | |
3324 | if (copy_from_compatible(&css_param->wb_config, arg->wb_config, | |
3325 | sizeof(struct atomisp_css_wb_config), | |
3326 | from_user)) | |
3327 | return -EFAULT; | |
3328 | css_param->update_flag.wb_config = | |
3329 | (struct atomisp_wb_config *) &css_param->wb_config; | |
3330 | } | |
3331 | ||
3332 | if (arg->ob_config && (from_user || !cur_config->ob_config)) { | |
3333 | if (copy_from_compatible(&css_param->ob_config, arg->ob_config, | |
3334 | sizeof(struct atomisp_css_ob_config), | |
3335 | from_user)) | |
3336 | return -EFAULT; | |
3337 | css_param->update_flag.ob_config = | |
3338 | (struct atomisp_ob_config *) &css_param->ob_config; | |
3339 | } | |
3340 | ||
3341 | if (arg->dp_config && (from_user || !cur_config->dp_config)) { | |
3342 | if (copy_from_compatible(&css_param->dp_config, arg->dp_config, | |
3343 | sizeof(struct atomisp_css_dp_config), | |
3344 | from_user)) | |
3345 | return -EFAULT; | |
3346 | css_param->update_flag.dp_config = | |
3347 | (struct atomisp_dp_config *) &css_param->dp_config; | |
3348 | } | |
3349 | ||
3350 | if (asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO) { | |
3351 | if (arg->dz_config && (from_user || !cur_config->dz_config)) { | |
3352 | if (copy_from_compatible(&css_param->dz_config, | |
3353 | arg->dz_config, | |
3354 | sizeof(struct atomisp_css_dz_config), | |
3355 | from_user)) | |
3356 | return -EFAULT; | |
3357 | if (!atomisp_check_zoom_region(asd, | |
3358 | &css_param->dz_config)) { | |
3359 | dev_err(asd->isp->dev, "crop region error!"); | |
3360 | return -EINVAL; | |
3361 | } | |
3362 | css_param->update_flag.dz_config = | |
3363 | (struct atomisp_dz_config *) | |
3364 | &css_param->dz_config; | |
3365 | } | |
3366 | } | |
3367 | ||
3368 | if (arg->nr_config && (from_user || !cur_config->nr_config)) { | |
3369 | if (copy_from_compatible(&css_param->nr_config, arg->nr_config, | |
3370 | sizeof(struct atomisp_css_nr_config), | |
3371 | from_user)) | |
3372 | return -EFAULT; | |
3373 | css_param->update_flag.nr_config = | |
3374 | (struct atomisp_nr_config *) &css_param->nr_config; | |
3375 | } | |
3376 | ||
3377 | if (arg->ee_config && (from_user || !cur_config->ee_config)) { | |
3378 | if (copy_from_compatible(&css_param->ee_config, arg->ee_config, | |
3379 | sizeof(struct atomisp_css_ee_config), | |
3380 | from_user)) | |
3381 | return -EFAULT; | |
3382 | css_param->update_flag.ee_config = | |
3383 | (struct atomisp_ee_config *) &css_param->ee_config; | |
3384 | } | |
3385 | ||
3386 | if (arg->tnr_config && (from_user || !cur_config->tnr_config)) { | |
3387 | if (copy_from_compatible(&css_param->tnr_config, | |
3388 | arg->tnr_config, | |
3389 | sizeof(struct atomisp_css_tnr_config), | |
3390 | from_user)) | |
3391 | return -EFAULT; | |
3392 | css_param->update_flag.tnr_config = | |
3393 | (struct atomisp_tnr_config *) | |
3394 | &css_param->tnr_config; | |
3395 | } | |
3396 | ||
3397 | if (arg->a3a_config && (from_user || !cur_config->a3a_config)) { | |
3398 | if (copy_from_compatible(&css_param->s3a_config, | |
3399 | arg->a3a_config, | |
3400 | sizeof(struct atomisp_css_3a_config), | |
3401 | from_user)) | |
3402 | return -EFAULT; | |
3403 | css_param->update_flag.a3a_config = | |
3404 | (struct atomisp_3a_config *) &css_param->s3a_config; | |
3405 | } | |
3406 | ||
3407 | if (arg->ctc_config && (from_user || !cur_config->ctc_config)) { | |
3408 | if (copy_from_compatible(&css_param->ctc_config, | |
3409 | arg->ctc_config, | |
3410 | sizeof(struct atomisp_css_ctc_config), | |
3411 | from_user)) | |
3412 | return -EFAULT; | |
3413 | css_param->update_flag.ctc_config = | |
3414 | (struct atomisp_ctc_config *) | |
3415 | &css_param->ctc_config; | |
3416 | } | |
3417 | ||
3418 | if (arg->cnr_config && (from_user || !cur_config->cnr_config)) { | |
3419 | if (copy_from_compatible(&css_param->cnr_config, | |
3420 | arg->cnr_config, | |
3421 | sizeof(struct atomisp_css_cnr_config), | |
3422 | from_user)) | |
3423 | return -EFAULT; | |
3424 | css_param->update_flag.cnr_config = | |
3425 | (struct atomisp_cnr_config *) | |
3426 | &css_param->cnr_config; | |
3427 | } | |
3428 | ||
3429 | if (arg->ecd_config && (from_user || !cur_config->ecd_config)) { | |
3430 | if (copy_from_compatible(&css_param->ecd_config, | |
3431 | arg->ecd_config, | |
3432 | sizeof(struct atomisp_css_ecd_config), | |
3433 | from_user)) | |
3434 | return -EFAULT; | |
3435 | css_param->update_flag.ecd_config = | |
3436 | (struct atomisp_ecd_config *) | |
3437 | &css_param->ecd_config; | |
3438 | } | |
3439 | ||
3440 | if (arg->ynr_config && (from_user || !cur_config->ynr_config)) { | |
3441 | if (copy_from_compatible(&css_param->ynr_config, | |
3442 | arg->ynr_config, | |
3443 | sizeof(struct atomisp_css_ynr_config), | |
3444 | from_user)) | |
3445 | return -EFAULT; | |
3446 | css_param->update_flag.ynr_config = | |
3447 | (struct atomisp_ynr_config *) | |
3448 | &css_param->ynr_config; | |
3449 | } | |
3450 | ||
3451 | if (arg->fc_config && (from_user || !cur_config->fc_config)) { | |
3452 | if (copy_from_compatible(&css_param->fc_config, | |
3453 | arg->fc_config, | |
3454 | sizeof(struct atomisp_css_fc_config), | |
3455 | from_user)) | |
3456 | return -EFAULT; | |
3457 | css_param->update_flag.fc_config = | |
3458 | (struct atomisp_fc_config *) &css_param->fc_config; | |
3459 | } | |
3460 | ||
3461 | if (arg->macc_config && (from_user || !cur_config->macc_config)) { | |
3462 | if (copy_from_compatible(&css_param->macc_config, | |
3463 | arg->macc_config, | |
3464 | sizeof(struct atomisp_css_macc_config), | |
3465 | from_user)) | |
3466 | return -EFAULT; | |
3467 | css_param->update_flag.macc_config = | |
3468 | (struct atomisp_macc_config *) | |
3469 | &css_param->macc_config; | |
3470 | } | |
3471 | ||
3472 | if (arg->aa_config && (from_user || !cur_config->aa_config)) { | |
3473 | if (copy_from_compatible(&css_param->aa_config, arg->aa_config, | |
3474 | sizeof(struct atomisp_css_aa_config), | |
3475 | from_user)) | |
3476 | return -EFAULT; | |
3477 | css_param->update_flag.aa_config = | |
3478 | (struct atomisp_aa_config *) &css_param->aa_config; | |
3479 | } | |
3480 | ||
3481 | if (arg->anr_config && (from_user || !cur_config->anr_config)) { | |
3482 | if (copy_from_compatible(&css_param->anr_config, | |
3483 | arg->anr_config, | |
3484 | sizeof(struct atomisp_css_anr_config), | |
3485 | from_user)) | |
3486 | return -EFAULT; | |
3487 | css_param->update_flag.anr_config = | |
3488 | (struct atomisp_anr_config *) | |
3489 | &css_param->anr_config; | |
3490 | } | |
3491 | ||
3492 | if (arg->xnr_config && (from_user || !cur_config->xnr_config)) { | |
3493 | if (copy_from_compatible(&css_param->xnr_config, | |
3494 | arg->xnr_config, | |
3495 | sizeof(struct atomisp_css_xnr_config), | |
3496 | from_user)) | |
3497 | return -EFAULT; | |
3498 | css_param->update_flag.xnr_config = | |
3499 | (struct atomisp_xnr_config *) | |
3500 | &css_param->xnr_config; | |
3501 | } | |
3502 | ||
3503 | if (arg->yuv2rgb_cc_config && | |
3504 | (from_user || !cur_config->yuv2rgb_cc_config)) { | |
3505 | if (copy_from_compatible(&css_param->yuv2rgb_cc_config, | |
3506 | arg->yuv2rgb_cc_config, | |
3507 | sizeof(struct atomisp_css_cc_config), | |
3508 | from_user)) | |
3509 | return -EFAULT; | |
3510 | css_param->update_flag.yuv2rgb_cc_config = | |
3511 | (struct atomisp_cc_config *) | |
3512 | &css_param->yuv2rgb_cc_config; | |
3513 | } | |
3514 | ||
3515 | if (arg->rgb2yuv_cc_config && | |
3516 | (from_user || !cur_config->rgb2yuv_cc_config)) { | |
3517 | if (copy_from_compatible(&css_param->rgb2yuv_cc_config, | |
3518 | arg->rgb2yuv_cc_config, | |
3519 | sizeof(struct atomisp_css_cc_config), | |
3520 | from_user)) | |
3521 | return -EFAULT; | |
3522 | css_param->update_flag.rgb2yuv_cc_config = | |
3523 | (struct atomisp_cc_config *) | |
3524 | &css_param->rgb2yuv_cc_config; | |
3525 | } | |
3526 | ||
3527 | if (arg->macc_table && (from_user || !cur_config->macc_table)) { | |
3528 | if (copy_from_compatible(&css_param->macc_table, | |
3529 | arg->macc_table, | |
3530 | sizeof(struct atomisp_css_macc_table), | |
3531 | from_user)) | |
3532 | return -EFAULT; | |
3533 | css_param->update_flag.macc_table = | |
3534 | (struct atomisp_macc_table *) | |
3535 | &css_param->macc_table; | |
3536 | } | |
3537 | ||
3538 | if (arg->xnr_table && (from_user || !cur_config->xnr_table)) { | |
3539 | if (copy_from_compatible(&css_param->xnr_table, | |
3540 | arg->xnr_table, | |
3541 | sizeof(struct atomisp_css_xnr_table), | |
3542 | from_user)) | |
3543 | return -EFAULT; | |
3544 | css_param->update_flag.xnr_table = | |
3545 | (struct atomisp_xnr_table *) &css_param->xnr_table; | |
3546 | } | |
3547 | ||
3548 | if (arg->r_gamma_table && (from_user || !cur_config->r_gamma_table)) { | |
3549 | if (copy_from_compatible(&css_param->r_gamma_table, | |
3550 | arg->r_gamma_table, | |
3551 | sizeof(struct atomisp_css_rgb_gamma_table), | |
3552 | from_user)) | |
3553 | return -EFAULT; | |
3554 | css_param->update_flag.r_gamma_table = | |
3555 | (struct atomisp_rgb_gamma_table *) | |
3556 | &css_param->r_gamma_table; | |
3557 | } | |
3558 | ||
3559 | if (arg->g_gamma_table && (from_user || !cur_config->g_gamma_table)) { | |
3560 | if (copy_from_compatible(&css_param->g_gamma_table, | |
3561 | arg->g_gamma_table, | |
3562 | sizeof(struct atomisp_css_rgb_gamma_table), | |
3563 | from_user)) | |
3564 | return -EFAULT; | |
3565 | css_param->update_flag.g_gamma_table = | |
3566 | (struct atomisp_rgb_gamma_table *) | |
3567 | &css_param->g_gamma_table; | |
3568 | } | |
3569 | ||
3570 | if (arg->b_gamma_table && (from_user || !cur_config->b_gamma_table)) { | |
3571 | if (copy_from_compatible(&css_param->b_gamma_table, | |
3572 | arg->b_gamma_table, | |
3573 | sizeof(struct atomisp_css_rgb_gamma_table), | |
3574 | from_user)) | |
3575 | return -EFAULT; | |
3576 | css_param->update_flag.b_gamma_table = | |
3577 | (struct atomisp_rgb_gamma_table *) | |
3578 | &css_param->b_gamma_table; | |
3579 | } | |
3580 | ||
3581 | if (arg->anr_thres && (from_user || !cur_config->anr_thres)) { | |
3582 | if (copy_from_compatible(&css_param->anr_thres, arg->anr_thres, | |
3583 | sizeof(struct atomisp_css_anr_thres), | |
3584 | from_user)) | |
3585 | return -EFAULT; | |
3586 | css_param->update_flag.anr_thres = | |
3587 | (struct atomisp_anr_thres *) &css_param->anr_thres; | |
3588 | } | |
3589 | ||
3590 | if (from_user) | |
3591 | css_param->isp_config_id = arg->isp_config_id; | |
3592 | /* | |
3593 | * These configurations are on used by ISP1.x, not for ISP2.x, | |
3594 | * so do not handle them. see comments of ia_css_isp_config. | |
3595 | * 1 cc_config | |
3596 | * 2 ce_config | |
3597 | * 3 de_config | |
3598 | * 4 gc_config | |
3599 | * 5 gamma_table | |
3600 | * 6 ctc_table | |
3601 | * 7 dvs_coefs | |
3602 | */ | |
3603 | return 0; | |
3604 | } | |
3605 | ||
3606 | int atomisp_cp_lsc_table(struct atomisp_sub_device *asd, | |
4995706d DY |
3607 | struct atomisp_shading_table *source_st, |
3608 | struct atomisp_css_params *css_param, | |
3609 | bool from_user) | |
a49d2536 AC |
3610 | { |
3611 | unsigned int i; | |
3612 | unsigned int len_table; | |
3613 | struct atomisp_css_shading_table *shading_table; | |
3614 | struct atomisp_css_shading_table *old_table; | |
3615 | #ifdef ISP2401 | |
3616 | struct atomisp_shading_table st; | |
3617 | #endif | |
3618 | ||
3619 | if (!source_st) | |
3620 | return 0; | |
3621 | ||
3622 | if (!css_param) | |
3623 | return -EINVAL; | |
3624 | ||
3625 | if (!from_user && css_param->update_flag.shading_table) | |
3626 | return 0; | |
3627 | ||
3628 | #ifdef ISP2401 | |
3629 | if (copy_from_compatible(&st, source_st, | |
3630 | sizeof(struct atomisp_shading_table), | |
3631 | from_user)) { | |
3632 | dev_err(asd->isp->dev, "copy shading table failed!"); | |
3633 | return -EFAULT; | |
3634 | } | |
3635 | ||
3636 | #endif | |
3637 | old_table = css_param->shading_table; | |
3638 | ||
3639 | #ifdef ISP2401 | |
3640 | ||
3641 | #endif | |
3642 | /* user config is to disable the shading table. */ | |
3643 | #ifndef ISP2401 | |
3644 | if (!source_st->enable) { | |
3645 | #else | |
3646 | if (!st.enable) { | |
3647 | #endif | |
3648 | /* Generate a minimum table with enable = 0. */ | |
3649 | shading_table = atomisp_css_shading_table_alloc(1, 1); | |
3650 | if (!shading_table) | |
3651 | return -ENOMEM; | |
3652 | shading_table->enable = 0; | |
3653 | goto set_lsc; | |
3654 | } | |
3655 | ||
3656 | /* Setting a new table. Validate first - all tables must be set */ | |
3657 | for (i = 0; i < ATOMISP_NUM_SC_COLORS; i++) { | |
3658 | #ifndef ISP2401 | |
3659 | if (!source_st->data[i]) | |
3660 | #else | |
3661 | if (!st.data[i]) { | |
3662 | dev_err(asd->isp->dev, "shading table validate failed"); | |
3663 | #endif | |
3664 | return -EINVAL; | |
3665 | #ifdef ISP2401 | |
3666 | } | |
3667 | #endif | |
3668 | } | |
3669 | ||
3670 | /* Shading table size per color */ | |
3671 | #ifndef ISP2401 | |
3672 | if (source_st->width > SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR || | |
3673 | source_st->height > SH_CSS_MAX_SCTBL_HEIGHT_PER_COLOR) | |
3674 | #else | |
3675 | if (st.width > SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR || | |
3676 | st.height > SH_CSS_MAX_SCTBL_HEIGHT_PER_COLOR) { | |
3677 | dev_err(asd->isp->dev, "shading table w/h validate failed!"); | |
3678 | #endif | |
3679 | return -EINVAL; | |
3680 | #ifdef ISP2401 | |
3681 | } | |
3682 | #endif | |
3683 | ||
3684 | #ifndef ISP2401 | |
3685 | shading_table = atomisp_css_shading_table_alloc(source_st->width, | |
3686 | source_st->height); | |
3687 | if (!shading_table) | |
3688 | return -ENOMEM; | |
3689 | #else | |
3690 | shading_table = atomisp_css_shading_table_alloc(st.width, | |
3691 | st.height); | |
3692 | if (!shading_table) { | |
3693 | dev_err(asd->isp->dev, "shading table alloc failed!"); | |
3694 | return -ENOMEM; | |
3695 | } | |
3696 | #endif | |
3697 | ||
3698 | #ifndef ISP2401 | |
3699 | len_table = source_st->width * source_st->height * ATOMISP_SC_TYPE_SIZE; | |
3700 | #else | |
3701 | len_table = st.width * st.height * ATOMISP_SC_TYPE_SIZE; | |
3702 | #endif | |
3703 | for (i = 0; i < ATOMISP_NUM_SC_COLORS; i++) { | |
3704 | if (copy_from_compatible(shading_table->data[i], | |
3705 | #ifndef ISP2401 | |
3706 | source_st->data[i], len_table, from_user)) { | |
3707 | #else | |
3708 | st.data[i], len_table, from_user)) { | |
3709 | #endif | |
3710 | atomisp_css_shading_table_free(shading_table); | |
3711 | return -EFAULT; | |
3712 | } | |
3713 | ||
3714 | } | |
3715 | #ifndef ISP2401 | |
3716 | shading_table->sensor_width = source_st->sensor_width; | |
3717 | shading_table->sensor_height = source_st->sensor_height; | |
3718 | shading_table->fraction_bits = source_st->fraction_bits; | |
3719 | shading_table->enable = source_st->enable; | |
3720 | #else | |
3721 | shading_table->sensor_width = st.sensor_width; | |
3722 | shading_table->sensor_height = st.sensor_height; | |
3723 | shading_table->fraction_bits = st.fraction_bits; | |
3724 | shading_table->enable = st.enable; | |
3725 | #endif | |
3726 | ||
3727 | /* No need to update shading table if it is the same */ | |
3728 | if (old_table != NULL && | |
3729 | old_table->sensor_width == shading_table->sensor_width && | |
3730 | old_table->sensor_height == shading_table->sensor_height && | |
3731 | old_table->width == shading_table->width && | |
3732 | old_table->height == shading_table->height && | |
3733 | old_table->fraction_bits == shading_table->fraction_bits && | |
3734 | old_table->enable == shading_table->enable) { | |
3735 | bool data_is_same = true; | |
3736 | ||
3737 | for (i = 0; i < ATOMISP_NUM_SC_COLORS; i++) { | |
3738 | if (memcmp(shading_table->data[i], old_table->data[i], | |
4995706d | 3739 | len_table) != 0) { |
a49d2536 AC |
3740 | data_is_same = false; |
3741 | break; | |
3742 | } | |
3743 | } | |
3744 | ||
3745 | if (data_is_same) { | |
3746 | atomisp_css_shading_table_free(shading_table); | |
3747 | return 0; | |
3748 | } | |
3749 | } | |
3750 | ||
3751 | set_lsc: | |
3752 | /* set LSC to CSS */ | |
3753 | css_param->shading_table = shading_table; | |
3754 | css_param->update_flag.shading_table = | |
3755 | (struct atomisp_shading_table *) shading_table; | |
3756 | asd->params.sc_en = shading_table != NULL; | |
3757 | ||
3758 | if (old_table) | |
3759 | atomisp_css_shading_table_free(old_table); | |
3760 | ||
3761 | return 0; | |
3762 | } | |
3763 | ||
3764 | int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, | |
3765 | struct ia_css_dvs2_coefficients *coefs, | |
3766 | struct atomisp_css_params *css_param, | |
3767 | bool from_user) | |
3768 | { | |
3769 | struct atomisp_css_dvs_grid_info *cur = | |
3770 | atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info); | |
3771 | int dvs_hor_coef_bytes, dvs_ver_coef_bytes; | |
3772 | #ifdef ISP2401 | |
3773 | struct ia_css_dvs2_coefficients dvs2_coefs; | |
3774 | #endif | |
3775 | ||
3776 | if (!coefs || !cur) | |
3777 | return 0; | |
3778 | ||
3779 | if (!from_user && css_param->update_flag.dvs2_coefs) | |
3780 | return 0; | |
3781 | ||
3782 | #ifndef ISP2401 | |
3783 | if (sizeof(*cur) != sizeof(coefs->grid) || | |
3784 | memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { | |
3785 | #else | |
3786 | if (copy_from_compatible(&dvs2_coefs, coefs, | |
3787 | sizeof(struct ia_css_dvs2_coefficients), | |
3788 | from_user)) { | |
3789 | dev_err(asd->isp->dev, "copy dvs2 coef failed"); | |
3790 | return -EFAULT; | |
3791 | } | |
3792 | ||
3793 | if (sizeof(*cur) != sizeof(dvs2_coefs.grid) || | |
3794 | memcmp(&dvs2_coefs.grid, cur, sizeof(dvs2_coefs.grid))) { | |
3795 | #endif | |
3796 | dev_err(asd->isp->dev, "dvs grid mis-match!\n"); | |
3797 | /* If the grid info in the argument differs from the current | |
3798 | grid info, we tell the caller to reset the grid size and | |
3799 | try again. */ | |
3800 | return -EAGAIN; | |
3801 | } | |
3802 | ||
3803 | #ifndef ISP2401 | |
3804 | if (coefs->hor_coefs.odd_real == NULL || | |
3805 | coefs->hor_coefs.odd_imag == NULL || | |
3806 | coefs->hor_coefs.even_real == NULL || | |
3807 | coefs->hor_coefs.even_imag == NULL || | |
3808 | coefs->ver_coefs.odd_real == NULL || | |
3809 | coefs->ver_coefs.odd_imag == NULL || | |
3810 | coefs->ver_coefs.even_real == NULL || | |
3811 | coefs->ver_coefs.even_imag == NULL) | |
3812 | #else | |
3813 | if (dvs2_coefs.hor_coefs.odd_real == NULL || | |
3814 | dvs2_coefs.hor_coefs.odd_imag == NULL || | |
3815 | dvs2_coefs.hor_coefs.even_real == NULL || | |
3816 | dvs2_coefs.hor_coefs.even_imag == NULL || | |
3817 | dvs2_coefs.ver_coefs.odd_real == NULL || | |
3818 | dvs2_coefs.ver_coefs.odd_imag == NULL || | |
3819 | dvs2_coefs.ver_coefs.even_real == NULL || | |
3820 | dvs2_coefs.ver_coefs.even_imag == NULL) | |
3821 | #endif | |
3822 | return -EINVAL; | |
3823 | ||
3824 | if (!css_param->dvs2_coeff) { | |
3825 | /* DIS coefficients. */ | |
3826 | css_param->dvs2_coeff = ia_css_dvs2_coefficients_allocate(cur); | |
3827 | if (!css_param->dvs2_coeff) | |
3828 | return -ENOMEM; | |
3829 | } | |
3830 | ||
3831 | dvs_hor_coef_bytes = asd->params.dvs_hor_coef_bytes; | |
3832 | dvs_ver_coef_bytes = asd->params.dvs_ver_coef_bytes; | |
3833 | if (copy_from_compatible(css_param->dvs2_coeff->hor_coefs.odd_real, | |
3834 | #ifndef ISP2401 | |
3835 | coefs->hor_coefs.odd_real, dvs_hor_coef_bytes, from_user) || | |
3836 | #else | |
3837 | dvs2_coefs.hor_coefs.odd_real, dvs_hor_coef_bytes, from_user) || | |
3838 | #endif | |
3839 | copy_from_compatible(css_param->dvs2_coeff->hor_coefs.odd_imag, | |
3840 | #ifndef ISP2401 | |
3841 | coefs->hor_coefs.odd_imag, dvs_hor_coef_bytes, from_user) || | |
3842 | #else | |
3843 | dvs2_coefs.hor_coefs.odd_imag, dvs_hor_coef_bytes, from_user) || | |
3844 | #endif | |
3845 | copy_from_compatible(css_param->dvs2_coeff->hor_coefs.even_real, | |
3846 | #ifndef ISP2401 | |
3847 | coefs->hor_coefs.even_real, dvs_hor_coef_bytes, from_user) || | |
3848 | #else | |
3849 | dvs2_coefs.hor_coefs.even_real, dvs_hor_coef_bytes, from_user) || | |
3850 | #endif | |
3851 | copy_from_compatible(css_param->dvs2_coeff->hor_coefs.even_imag, | |
3852 | #ifndef ISP2401 | |
3853 | coefs->hor_coefs.even_imag, dvs_hor_coef_bytes, from_user) || | |
3854 | #else | |
3855 | dvs2_coefs.hor_coefs.even_imag, dvs_hor_coef_bytes, from_user) || | |
3856 | #endif | |
3857 | copy_from_compatible(css_param->dvs2_coeff->ver_coefs.odd_real, | |
3858 | #ifndef ISP2401 | |
3859 | coefs->ver_coefs.odd_real, dvs_ver_coef_bytes, from_user) || | |
3860 | #else | |
3861 | dvs2_coefs.ver_coefs.odd_real, dvs_ver_coef_bytes, from_user) || | |
3862 | #endif | |
3863 | copy_from_compatible(css_param->dvs2_coeff->ver_coefs.odd_imag, | |
3864 | #ifndef ISP2401 | |
3865 | coefs->ver_coefs.odd_imag, dvs_ver_coef_bytes, from_user) || | |
3866 | #else | |
3867 | dvs2_coefs.ver_coefs.odd_imag, dvs_ver_coef_bytes, from_user) || | |
3868 | #endif | |
3869 | copy_from_compatible(css_param->dvs2_coeff->ver_coefs.even_real, | |
3870 | #ifndef ISP2401 | |
3871 | coefs->ver_coefs.even_real, dvs_ver_coef_bytes, from_user) || | |
3872 | #else | |
3873 | dvs2_coefs.ver_coefs.even_real, dvs_ver_coef_bytes, from_user) || | |
3874 | #endif | |
3875 | copy_from_compatible(css_param->dvs2_coeff->ver_coefs.even_imag, | |
3876 | #ifndef ISP2401 | |
3877 | coefs->ver_coefs.even_imag, dvs_ver_coef_bytes, from_user)) { | |
3878 | #else | |
3879 | dvs2_coefs.ver_coefs.even_imag, dvs_ver_coef_bytes, from_user)) { | |
3880 | #endif | |
3881 | ia_css_dvs2_coefficients_free(css_param->dvs2_coeff); | |
3882 | css_param->dvs2_coeff = NULL; | |
3883 | return -EFAULT; | |
3884 | } | |
3885 | ||
3886 | css_param->update_flag.dvs2_coefs = | |
3887 | (struct atomisp_dvs2_coefficients *)css_param->dvs2_coeff; | |
3888 | return 0; | |
3889 | } | |
3890 | ||
3891 | int atomisp_cp_dvs_6axis_config(struct atomisp_sub_device *asd, | |
3892 | struct atomisp_dvs_6axis_config *source_6axis_config, | |
3893 | struct atomisp_css_params *css_param, | |
3894 | bool from_user) | |
3895 | { | |
3896 | struct atomisp_css_dvs_6axis_config *dvs_6axis_config; | |
3897 | struct atomisp_css_dvs_6axis_config *old_6axis_config; | |
3898 | #ifdef ISP2401 | |
3899 | struct atomisp_css_dvs_6axis_config t_6axis_config; | |
3900 | #endif | |
3901 | struct ia_css_stream *stream = | |
3902 | asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream; | |
3903 | struct atomisp_css_dvs_grid_info *dvs_grid_info = | |
3904 | atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info); | |
3905 | int ret = -EFAULT; | |
3906 | ||
3907 | if (stream == NULL) { | |
3908 | dev_err(asd->isp->dev, "%s: internal error!", __func__); | |
3909 | return -EINVAL; | |
3910 | } | |
3911 | ||
3912 | if (!source_6axis_config || !dvs_grid_info) | |
3913 | return 0; | |
3914 | ||
3915 | if (!dvs_grid_info->enable) | |
3916 | return 0; | |
3917 | ||
3918 | if (!from_user && css_param->update_flag.dvs_6axis_config) | |
3919 | return 0; | |
3920 | ||
3921 | /* check whether need to reallocate for 6 axis config */ | |
3922 | old_6axis_config = css_param->dvs_6axis; | |
3923 | dvs_6axis_config = old_6axis_config; | |
3924 | #ifdef ISP2401 | |
3925 | ||
3926 | if (copy_from_compatible(&t_6axis_config, source_6axis_config, | |
3927 | sizeof(struct atomisp_dvs_6axis_config), | |
3928 | from_user)) { | |
3929 | dev_err(asd->isp->dev, "copy morph table failed!"); | |
3930 | return -EFAULT; | |
3931 | } | |
3932 | ||
3933 | #endif | |
3934 | if (old_6axis_config && | |
3935 | #ifndef ISP2401 | |
3936 | (old_6axis_config->width_y != source_6axis_config->width_y || | |
3937 | old_6axis_config->height_y != source_6axis_config->height_y || | |
3938 | old_6axis_config->width_uv != source_6axis_config->width_uv || | |
3939 | old_6axis_config->height_uv != source_6axis_config->height_uv)) { | |
3940 | #else | |
3941 | (old_6axis_config->width_y != t_6axis_config.width_y || | |
3942 | old_6axis_config->height_y != t_6axis_config.height_y || | |
3943 | old_6axis_config->width_uv != t_6axis_config.width_uv || | |
3944 | old_6axis_config->height_uv != t_6axis_config.height_uv)) { | |
3945 | #endif | |
3946 | ia_css_dvs2_6axis_config_free(css_param->dvs_6axis); | |
3947 | css_param->dvs_6axis = NULL; | |
3948 | ||
3949 | dvs_6axis_config = ia_css_dvs2_6axis_config_allocate(stream); | |
3950 | if (!dvs_6axis_config) | |
3951 | return -ENOMEM; | |
3952 | } else if (!dvs_6axis_config) { | |
3953 | dvs_6axis_config = ia_css_dvs2_6axis_config_allocate(stream); | |
3954 | if (!dvs_6axis_config) | |
3955 | return -ENOMEM; | |
3956 | } | |
3957 | ||
3958 | #ifndef ISP2401 | |
3959 | dvs_6axis_config->exp_id = source_6axis_config->exp_id; | |
3960 | #else | |
3961 | dvs_6axis_config->exp_id = t_6axis_config.exp_id; | |
3962 | #endif | |
3963 | ||
3964 | if (copy_from_compatible(dvs_6axis_config->xcoords_y, | |
3965 | #ifndef ISP2401 | |
3966 | source_6axis_config->xcoords_y, | |
3967 | source_6axis_config->width_y * | |
3968 | source_6axis_config->height_y * | |
3969 | sizeof(*source_6axis_config->xcoords_y), | |
3970 | #else | |
3971 | t_6axis_config.xcoords_y, | |
3972 | t_6axis_config.width_y * | |
3973 | t_6axis_config.height_y * | |
3974 | sizeof(*dvs_6axis_config->xcoords_y), | |
3975 | #endif | |
3976 | from_user)) | |
3977 | goto error; | |
3978 | if (copy_from_compatible(dvs_6axis_config->ycoords_y, | |
3979 | #ifndef ISP2401 | |
3980 | source_6axis_config->ycoords_y, | |
3981 | source_6axis_config->width_y * | |
3982 | source_6axis_config->height_y * | |
3983 | sizeof(*source_6axis_config->ycoords_y), | |
3984 | #else | |
3985 | t_6axis_config.ycoords_y, | |
3986 | t_6axis_config.width_y * | |
3987 | t_6axis_config.height_y * | |
3988 | sizeof(*dvs_6axis_config->ycoords_y), | |
3989 | #endif | |
3990 | from_user)) | |
3991 | goto error; | |
3992 | if (copy_from_compatible(dvs_6axis_config->xcoords_uv, | |
3993 | #ifndef ISP2401 | |
3994 | source_6axis_config->xcoords_uv, | |
3995 | source_6axis_config->width_uv * | |
3996 | source_6axis_config->height_uv * | |
3997 | sizeof(*source_6axis_config->xcoords_uv), | |
3998 | #else | |
3999 | t_6axis_config.xcoords_uv, | |
4000 | t_6axis_config.width_uv * | |
4001 | t_6axis_config.height_uv * | |
4002 | sizeof(*dvs_6axis_config->xcoords_uv), | |
4003 | #endif | |
4004 | from_user)) | |
4005 | goto error; | |
4006 | if (copy_from_compatible(dvs_6axis_config->ycoords_uv, | |
4007 | #ifndef ISP2401 | |
4008 | source_6axis_config->ycoords_uv, | |
4009 | source_6axis_config->width_uv * | |
4010 | source_6axis_config->height_uv * | |
4011 | sizeof(*source_6axis_config->ycoords_uv), | |
4012 | #else | |
4013 | t_6axis_config.ycoords_uv, | |
4014 | t_6axis_config.width_uv * | |
4015 | t_6axis_config.height_uv * | |
4016 | sizeof(*dvs_6axis_config->ycoords_uv), | |
4017 | #endif | |
4018 | from_user)) | |
4019 | goto error; | |
4020 | ||
4021 | css_param->dvs_6axis = dvs_6axis_config; | |
4022 | css_param->update_flag.dvs_6axis_config = | |
4023 | (struct atomisp_dvs_6axis_config *) dvs_6axis_config; | |
4024 | return 0; | |
4025 | ||
4026 | error: | |
4027 | if (dvs_6axis_config) | |
4028 | ia_css_dvs2_6axis_config_free(dvs_6axis_config); | |
4029 | return ret; | |
4030 | } | |
4031 | ||
4032 | int atomisp_cp_morph_table(struct atomisp_sub_device *asd, | |
4033 | struct atomisp_morph_table *source_morph_table, | |
4034 | struct atomisp_css_params *css_param, | |
4035 | bool from_user) | |
4036 | { | |
4037 | int ret = -EFAULT; | |
4038 | unsigned int i; | |
4039 | struct atomisp_css_morph_table *morph_table; | |
4040 | #ifdef ISP2401 | |
4041 | struct atomisp_css_morph_table mtbl; | |
4042 | #endif | |
4043 | struct atomisp_css_morph_table *old_morph_table; | |
4044 | ||
4045 | if (!source_morph_table) | |
4046 | return 0; | |
4047 | ||
4048 | if (!from_user && css_param->update_flag.morph_table) | |
4049 | return 0; | |
4050 | ||
4051 | old_morph_table = css_param->morph_table; | |
4052 | ||
4053 | #ifdef ISP2401 | |
4054 | if (copy_from_compatible(&mtbl, source_morph_table, | |
4055 | sizeof(struct atomisp_morph_table), | |
4056 | from_user)) { | |
4057 | dev_err(asd->isp->dev, "copy morph table failed!"); | |
4058 | return -EFAULT; | |
4059 | } | |
4060 | ||
4061 | #endif | |
4062 | morph_table = atomisp_css_morph_table_allocate( | |
4063 | #ifndef ISP2401 | |
4064 | source_morph_table->width, | |
4065 | source_morph_table->height); | |
4066 | #else | |
4067 | mtbl.width, | |
4068 | mtbl.height); | |
4069 | #endif | |
4070 | if (!morph_table) | |
4071 | return -ENOMEM; | |
4072 | ||
4073 | for (i = 0; i < CSS_MORPH_TABLE_NUM_PLANES; i++) { | |
4074 | if (copy_from_compatible(morph_table->coordinates_x[i], | |
4075 | source_morph_table->coordinates_x[i], | |
4076 | #ifndef ISP2401 | |
4077 | source_morph_table->height * source_morph_table->width * | |
4078 | sizeof(*source_morph_table->coordinates_x[i]), | |
4079 | #else | |
4080 | mtbl.height * mtbl.width * | |
4081 | sizeof(*morph_table->coordinates_x[i]), | |
4082 | #endif | |
4083 | from_user)) | |
4084 | goto error; | |
4085 | ||
4086 | if (copy_from_compatible(morph_table->coordinates_y[i], | |
4087 | source_morph_table->coordinates_y[i], | |
4088 | #ifndef ISP2401 | |
4089 | source_morph_table->height * source_morph_table->width * | |
4090 | sizeof(*source_morph_table->coordinates_y[i]), | |
4091 | #else | |
4092 | mtbl.height * mtbl.width * | |
4093 | sizeof(*morph_table->coordinates_y[i]), | |
4094 | #endif | |
4095 | from_user)) | |
4096 | goto error; | |
4097 | } | |
4098 | ||
4099 | css_param->morph_table = morph_table; | |
4100 | if (old_morph_table) | |
4101 | atomisp_css_morph_table_free(old_morph_table); | |
4102 | css_param->update_flag.morph_table = | |
4103 | (struct atomisp_morph_table *) morph_table; | |
4104 | return 0; | |
4105 | ||
4106 | error: | |
4107 | if (morph_table) | |
4108 | atomisp_css_morph_table_free(morph_table); | |
4109 | return ret; | |
4110 | } | |
4111 | ||
4112 | int atomisp_makeup_css_parameters(struct atomisp_sub_device *asd, | |
4113 | struct atomisp_parameters *arg, | |
4114 | struct atomisp_css_params *css_param) | |
4115 | { | |
4116 | int ret; | |
4117 | ||
4118 | ret = atomisp_cp_general_isp_parameters(asd, arg, css_param, false); | |
4119 | if (ret) | |
4120 | return ret; | |
4121 | ret = atomisp_cp_lsc_table(asd, arg->shading_table, css_param, false); | |
4122 | if (ret) | |
4123 | return ret; | |
4124 | ret = atomisp_cp_morph_table(asd, arg->morph_table, css_param, false); | |
4125 | if (ret) | |
4126 | return ret; | |
4127 | ret = atomisp_css_cp_dvs2_coefs(asd, | |
4128 | (struct ia_css_dvs2_coefficients *) arg->dvs2_coefs, | |
4129 | css_param, false); | |
4130 | if (ret) | |
4131 | return ret; | |
4132 | ret = atomisp_cp_dvs_6axis_config(asd, arg->dvs_6axis_config, | |
4133 | css_param, false); | |
4134 | return ret; | |
4135 | } | |
4136 | ||
4137 | void atomisp_free_css_parameters(struct atomisp_css_params *css_param) | |
4138 | { | |
4139 | if (css_param->dvs_6axis) { | |
4140 | ia_css_dvs2_6axis_config_free(css_param->dvs_6axis); | |
4141 | css_param->dvs_6axis = NULL; | |
4142 | } | |
4143 | if (css_param->dvs2_coeff) { | |
4144 | ia_css_dvs2_coefficients_free(css_param->dvs2_coeff); | |
4145 | css_param->dvs2_coeff = NULL; | |
4146 | } | |
4147 | if (css_param->shading_table) { | |
4148 | ia_css_shading_table_free(css_param->shading_table); | |
4149 | css_param->shading_table = NULL; | |
4150 | } | |
4151 | if (css_param->morph_table) { | |
4152 | ia_css_morph_table_free(css_param->morph_table); | |
4153 | css_param->morph_table = NULL; | |
4154 | } | |
4155 | } | |
4156 | ||
4157 | /* | |
4158 | * Check parameter queue list and buffer queue list to find out if matched items | |
4159 | * and then set parameter to CSS and enqueue buffer to CSS. | |
4160 | * Of course, if the buffer in buffer waiting list is not bound to a per-frame | |
4161 | * parameter, it will be enqueued into CSS as long as the per-frame setting | |
4162 | * buffers before it get enqueued. | |
4163 | */ | |
4164 | void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) | |
4165 | { | |
4166 | struct atomisp_sub_device *asd = pipe->asd; | |
4167 | struct videobuf_buffer *vb = NULL, *vb_tmp; | |
4168 | struct atomisp_css_params_with_list *param = NULL, *param_tmp; | |
4169 | struct videobuf_vmalloc_memory *vm_mem = NULL; | |
4170 | unsigned long irqflags; | |
4171 | bool need_to_enqueue_buffer = false; | |
4172 | ||
4173 | if (atomisp_is_vf_pipe(pipe)) | |
4174 | return; | |
4175 | ||
4176 | /* | |
4177 | * CSS/FW requires set parameter and enqueue buffer happen after ISP | |
4178 | * is streamon. | |
4179 | */ | |
4180 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
4181 | return; | |
4182 | ||
4183 | if (list_empty(&pipe->per_frame_params) || | |
4184 | list_empty(&pipe->buffers_waiting_for_param)) | |
4185 | return; | |
4186 | ||
4187 | list_for_each_entry_safe(vb, vb_tmp, | |
4188 | &pipe->buffers_waiting_for_param, queue) { | |
4189 | if (pipe->frame_request_config_id[vb->i]) { | |
4190 | list_for_each_entry_safe(param, param_tmp, | |
4191 | &pipe->per_frame_params, list) { | |
4192 | if (pipe->frame_request_config_id[vb->i] != | |
4193 | param->params.isp_config_id) | |
4194 | continue; | |
4195 | ||
4196 | list_del(¶m->list); | |
4197 | list_del(&vb->queue); | |
4198 | /* | |
4199 | * clear the request config id as the buffer | |
4200 | * will be handled and enqueued into CSS soon | |
4201 | */ | |
4202 | pipe->frame_request_config_id[vb->i] = 0; | |
4203 | pipe->frame_params[vb->i] = param; | |
4204 | vm_mem = vb->priv; | |
4205 | BUG_ON(!vm_mem); | |
4206 | break; | |
4207 | } | |
4208 | ||
4209 | if (vm_mem) { | |
4210 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
4211 | list_add_tail(&vb->queue, &pipe->activeq); | |
4212 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
4213 | vm_mem = NULL; | |
4214 | need_to_enqueue_buffer = true; | |
4215 | } else { | |
4216 | /* The is the end, stop further loop */ | |
4217 | break; | |
4218 | } | |
4219 | } else { | |
4220 | list_del(&vb->queue); | |
4221 | pipe->frame_params[vb->i] = NULL; | |
4222 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
4223 | list_add_tail(&vb->queue, &pipe->activeq); | |
4224 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
4225 | need_to_enqueue_buffer = true; | |
4226 | } | |
4227 | } | |
4228 | ||
4229 | if (need_to_enqueue_buffer) { | |
4230 | atomisp_qbuffers_to_css(asd); | |
4231 | #ifndef ISP2401 | |
4232 | if (!atomisp_is_wdt_running(asd) && atomisp_buffers_queued(asd)) | |
4233 | atomisp_wdt_start(asd); | |
4234 | #else | |
4235 | if (atomisp_buffers_queued_pipe(pipe)) { | |
4236 | if (!atomisp_is_wdt_running(pipe)) | |
4237 | atomisp_wdt_start(pipe); | |
4238 | else | |
4239 | atomisp_wdt_refresh_pipe(pipe, | |
4240 | ATOMISP_WDT_KEEP_CURRENT_DELAY); | |
4241 | } | |
4242 | #endif | |
4243 | } | |
4244 | } | |
4245 | ||
4246 | /* | |
4247 | * Function to configure ISP parameters | |
4248 | */ | |
4249 | int atomisp_set_parameters(struct video_device *vdev, | |
4250 | struct atomisp_parameters *arg) | |
4251 | { | |
4252 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
4253 | struct atomisp_sub_device *asd = pipe->asd; | |
4254 | struct atomisp_css_params_with_list *param = NULL; | |
4255 | struct atomisp_css_params *css_param = &asd->params.css_param; | |
4256 | int ret; | |
4257 | ||
4258 | if (asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream == NULL) { | |
4259 | dev_err(asd->isp->dev, "%s: internal error!\n", __func__); | |
4260 | return -EINVAL; | |
4261 | } | |
4262 | ||
4263 | dev_dbg(asd->isp->dev, "%s: set parameter(per_frame_setting %d) for asd%d with isp_config_id %d of %s\n", | |
4264 | __func__, arg->per_frame_setting, asd->index, | |
4265 | arg->isp_config_id, vdev->name); | |
4266 | #ifdef ISP2401 | |
4267 | ||
4268 | if (atomisp_is_vf_pipe(pipe) && arg->per_frame_setting) { | |
4269 | dev_err(asd->isp->dev, "%s: vf pipe not support per_frame_setting", | |
4270 | __func__); | |
4271 | return -EINVAL; | |
4272 | } | |
4273 | ||
4274 | #endif | |
4275 | if (arg->per_frame_setting && !atomisp_is_vf_pipe(pipe)) { | |
4276 | /* | |
4277 | * Per-frame setting enabled, we allocate a new paramter | |
4278 | * buffer to cache the parameters and only when frame buffers | |
4279 | * are ready, the parameters will be set to CSS. | |
4280 | * per-frame setting only works for the main output frame. | |
4281 | */ | |
df0347f3 | 4282 | param = kvzalloc(sizeof(*param), GFP_KERNEL); |
a49d2536 AC |
4283 | if (!param) { |
4284 | dev_err(asd->isp->dev, "%s: failed to alloc params buffer\n", | |
4285 | __func__); | |
4286 | return -ENOMEM; | |
4287 | } | |
4288 | css_param = ¶m->params; | |
4289 | } | |
4290 | ||
4291 | ret = atomisp_cp_general_isp_parameters(asd, arg, css_param, true); | |
4292 | if (ret) | |
4293 | goto apply_parameter_failed; | |
4294 | ||
4295 | ret = atomisp_cp_lsc_table(asd, arg->shading_table, css_param, true); | |
4296 | if (ret) | |
4297 | goto apply_parameter_failed; | |
4298 | ||
4299 | ret = atomisp_cp_morph_table(asd, arg->morph_table, css_param, true); | |
4300 | if (ret) | |
4301 | goto apply_parameter_failed; | |
4302 | ||
4303 | ret = atomisp_css_cp_dvs2_coefs(asd, | |
4304 | (struct ia_css_dvs2_coefficients *) arg->dvs2_coefs, | |
4305 | css_param, true); | |
4306 | if (ret) | |
4307 | goto apply_parameter_failed; | |
4308 | ||
4309 | ret = atomisp_cp_dvs_6axis_config(asd, arg->dvs_6axis_config, | |
4310 | css_param, true); | |
4311 | if (ret) | |
4312 | goto apply_parameter_failed; | |
4313 | ||
4314 | if (!(arg->per_frame_setting && !atomisp_is_vf_pipe(pipe))) { | |
4315 | /* indicate to CSS that we have parameters to be updated */ | |
4316 | asd->params.css_update_params_needed = true; | |
4317 | } else { | |
4318 | list_add_tail(¶m->list, &pipe->per_frame_params); | |
4319 | atomisp_handle_parameter_and_buffer(pipe); | |
4320 | } | |
4321 | ||
4322 | return 0; | |
4323 | ||
4324 | apply_parameter_failed: | |
4325 | if (css_param) | |
4326 | atomisp_free_css_parameters(css_param); | |
4327 | if (param) | |
05f1d92f | 4328 | kvfree(param); |
a49d2536 AC |
4329 | |
4330 | return ret; | |
4331 | } | |
4332 | ||
4333 | /* | |
4334 | * Function to set/get isp parameters to isp | |
4335 | */ | |
4336 | int atomisp_param(struct atomisp_sub_device *asd, int flag, | |
4337 | struct atomisp_parm *config) | |
4338 | { | |
4339 | struct atomisp_device *isp = asd->isp; | |
4340 | struct ia_css_pipe_config *vp_cfg = | |
4341 | &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. | |
4342 | pipe_configs[IA_CSS_PIPE_ID_VIDEO]; | |
4343 | ||
4344 | /* Read parameter for 3A binary info */ | |
4345 | if (flag == 0) { | |
4346 | struct atomisp_css_dvs_grid_info *dvs_grid_info = | |
4347 | atomisp_css_get_dvs_grid_info( | |
4348 | &asd->params.curr_grid_info); | |
4349 | ||
4350 | if (&config->info == NULL) { | |
4351 | dev_err(isp->dev, "ERROR: NULL pointer in grid_info\n"); | |
4352 | return -EINVAL; | |
4353 | } | |
4354 | atomisp_curr_user_grid_info(asd, &config->info); | |
4355 | ||
4356 | /* We always return the resolution and stride even if there is | |
4357 | * no valid metadata. This allows the caller to get the | |
4358 | * information needed to allocate user-space buffers. */ | |
4359 | config->metadata_config.metadata_height = asd-> | |
4360 | stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. | |
4361 | metadata_info.resolution.height; | |
4362 | config->metadata_config.metadata_stride = asd-> | |
4363 | stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. | |
4364 | metadata_info.stride; | |
4365 | ||
4366 | /* update dvs grid info */ | |
4367 | if (dvs_grid_info) | |
4368 | memcpy(&config->dvs_grid, | |
4369 | dvs_grid_info, | |
4370 | sizeof(struct atomisp_css_dvs_grid_info)); | |
4371 | ||
4372 | if (asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO) { | |
4373 | config->dvs_envelop.width = 0; | |
4374 | config->dvs_envelop.height = 0; | |
4375 | return 0; | |
4376 | } | |
4377 | ||
4378 | /* update dvs envelop info */ | |
4379 | if (!asd->continuous_mode->val) { | |
4380 | config->dvs_envelop.width = vp_cfg->dvs_envelope.width; | |
4381 | config->dvs_envelop.height = | |
4382 | vp_cfg->dvs_envelope.height; | |
4383 | } else { | |
4384 | unsigned int dvs_w, dvs_h, dvs_w_max, dvs_h_max; | |
4385 | ||
4386 | dvs_w = vp_cfg->bayer_ds_out_res.width - | |
4387 | vp_cfg->output_info[0].res.width; | |
4388 | dvs_h = vp_cfg->bayer_ds_out_res.height - | |
4389 | vp_cfg->output_info[0].res.height; | |
4390 | dvs_w_max = rounddown( | |
4391 | vp_cfg->output_info[0].res.width / 5, | |
4392 | ATOM_ISP_STEP_WIDTH); | |
4393 | dvs_h_max = rounddown( | |
4394 | vp_cfg->output_info[0].res.height / 5, | |
4395 | ATOM_ISP_STEP_HEIGHT); | |
4396 | ||
4397 | config->dvs_envelop.width = min(dvs_w, dvs_w_max); | |
4398 | config->dvs_envelop.height = min(dvs_h, dvs_h_max); | |
4399 | } | |
4400 | ||
4401 | return 0; | |
4402 | } | |
4403 | ||
4404 | memcpy(&asd->params.css_param.wb_config, &config->wb_config, | |
4405 | sizeof(struct atomisp_css_wb_config)); | |
4406 | memcpy(&asd->params.css_param.ob_config, &config->ob_config, | |
4407 | sizeof(struct atomisp_css_ob_config)); | |
4408 | memcpy(&asd->params.css_param.dp_config, &config->dp_config, | |
4409 | sizeof(struct atomisp_css_dp_config)); | |
4410 | memcpy(&asd->params.css_param.de_config, &config->de_config, | |
4411 | sizeof(struct atomisp_css_de_config)); | |
4412 | memcpy(&asd->params.css_param.dz_config, &config->dz_config, | |
4413 | sizeof(struct atomisp_css_dz_config)); | |
4414 | memcpy(&asd->params.css_param.ce_config, &config->ce_config, | |
4415 | sizeof(struct atomisp_css_ce_config)); | |
4416 | memcpy(&asd->params.css_param.nr_config, &config->nr_config, | |
4417 | sizeof(struct atomisp_css_nr_config)); | |
4418 | memcpy(&asd->params.css_param.ee_config, &config->ee_config, | |
4419 | sizeof(struct atomisp_css_ee_config)); | |
4420 | memcpy(&asd->params.css_param.tnr_config, &config->tnr_config, | |
4421 | sizeof(struct atomisp_css_tnr_config)); | |
4422 | ||
4423 | if (asd->params.color_effect == V4L2_COLORFX_NEGATIVE) { | |
4424 | asd->params.css_param.cc_config.matrix[3] = -config->cc_config.matrix[3]; | |
4425 | asd->params.css_param.cc_config.matrix[4] = -config->cc_config.matrix[4]; | |
4426 | asd->params.css_param.cc_config.matrix[5] = -config->cc_config.matrix[5]; | |
4427 | asd->params.css_param.cc_config.matrix[6] = -config->cc_config.matrix[6]; | |
4428 | asd->params.css_param.cc_config.matrix[7] = -config->cc_config.matrix[7]; | |
4429 | asd->params.css_param.cc_config.matrix[8] = -config->cc_config.matrix[8]; | |
4430 | } | |
4431 | ||
4432 | if (asd->params.color_effect != V4L2_COLORFX_SEPIA && | |
4433 | asd->params.color_effect != V4L2_COLORFX_BW) { | |
4434 | memcpy(&asd->params.css_param.cc_config, &config->cc_config, | |
4435 | sizeof(struct atomisp_css_cc_config)); | |
4436 | atomisp_css_set_cc_config(asd, &asd->params.css_param.cc_config); | |
4437 | } | |
4438 | ||
4439 | atomisp_css_set_wb_config(asd, &asd->params.css_param.wb_config); | |
4440 | atomisp_css_set_ob_config(asd, &asd->params.css_param.ob_config); | |
4441 | atomisp_css_set_de_config(asd, &asd->params.css_param.de_config); | |
4442 | atomisp_css_set_dz_config(asd, &asd->params.css_param.dz_config); | |
4443 | atomisp_css_set_ce_config(asd, &asd->params.css_param.ce_config); | |
4444 | atomisp_css_set_dp_config(asd, &asd->params.css_param.dp_config); | |
4445 | atomisp_css_set_nr_config(asd, &asd->params.css_param.nr_config); | |
4446 | atomisp_css_set_ee_config(asd, &asd->params.css_param.ee_config); | |
4447 | atomisp_css_set_tnr_config(asd, &asd->params.css_param.tnr_config); | |
4448 | asd->params.css_update_params_needed = true; | |
4449 | ||
4450 | return 0; | |
4451 | } | |
4452 | ||
4453 | /* | |
4454 | * Function to configure color effect of the image | |
4455 | */ | |
4456 | int atomisp_color_effect(struct atomisp_sub_device *asd, int flag, | |
4457 | __s32 *effect) | |
4458 | { | |
4459 | struct atomisp_css_cc_config *cc_config = NULL; | |
4460 | struct atomisp_css_macc_table *macc_table = NULL; | |
4461 | struct atomisp_css_ctc_table *ctc_table = NULL; | |
4462 | int ret = 0; | |
4463 | struct v4l2_control control; | |
4464 | struct atomisp_device *isp = asd->isp; | |
4465 | ||
4466 | if (flag == 0) { | |
4467 | *effect = asd->params.color_effect; | |
4468 | return 0; | |
4469 | } | |
4470 | ||
4471 | ||
4472 | control.id = V4L2_CID_COLORFX; | |
4473 | control.value = *effect; | |
4474 | ret = | |
4475 | v4l2_s_ctrl(NULL, isp->inputs[asd->input_curr].camera->ctrl_handler, | |
4476 | &control); | |
4477 | /* | |
4478 | * if set color effect to sensor successfully, return | |
4479 | * 0 directly. | |
4480 | */ | |
4481 | if (!ret) { | |
4482 | asd->params.color_effect = (u32)*effect; | |
4483 | return 0; | |
4484 | } | |
4485 | ||
4486 | if (*effect == asd->params.color_effect) | |
4487 | return 0; | |
4488 | ||
4489 | /* | |
4490 | * isp_subdev->params.macc_en should be set to false. | |
4491 | */ | |
4492 | asd->params.macc_en = false; | |
4493 | ||
4494 | switch (*effect) { | |
4495 | case V4L2_COLORFX_NONE: | |
4496 | macc_table = &asd->params.css_param.macc_table; | |
4497 | asd->params.macc_en = true; | |
4498 | break; | |
4499 | case V4L2_COLORFX_SEPIA: | |
4500 | cc_config = &sepia_cc_config; | |
4501 | break; | |
4502 | case V4L2_COLORFX_NEGATIVE: | |
4503 | cc_config = &nega_cc_config; | |
4504 | break; | |
4505 | case V4L2_COLORFX_BW: | |
4506 | cc_config = &mono_cc_config; | |
4507 | break; | |
4508 | case V4L2_COLORFX_SKY_BLUE: | |
4509 | macc_table = &blue_macc_table; | |
4510 | asd->params.macc_en = true; | |
4511 | break; | |
4512 | case V4L2_COLORFX_GRASS_GREEN: | |
4513 | macc_table = &green_macc_table; | |
4514 | asd->params.macc_en = true; | |
4515 | break; | |
4516 | case V4L2_COLORFX_SKIN_WHITEN_LOW: | |
4517 | macc_table = &skin_low_macc_table; | |
4518 | asd->params.macc_en = true; | |
4519 | break; | |
4520 | case V4L2_COLORFX_SKIN_WHITEN: | |
4521 | macc_table = &skin_medium_macc_table; | |
4522 | asd->params.macc_en = true; | |
4523 | break; | |
4524 | case V4L2_COLORFX_SKIN_WHITEN_HIGH: | |
4525 | macc_table = &skin_high_macc_table; | |
4526 | asd->params.macc_en = true; | |
4527 | break; | |
4528 | case V4L2_COLORFX_VIVID: | |
4529 | ctc_table = &vivid_ctc_table; | |
4530 | break; | |
4531 | default: | |
4532 | return -EINVAL; | |
4533 | } | |
4534 | atomisp_update_capture_mode(asd); | |
4535 | ||
4536 | if (cc_config) | |
4537 | atomisp_css_set_cc_config(asd, cc_config); | |
4538 | if (macc_table) | |
4539 | atomisp_css_set_macc_table(asd, macc_table); | |
4540 | if (ctc_table) | |
4541 | atomisp_css_set_ctc_table(asd, ctc_table); | |
4542 | asd->params.color_effect = (u32)*effect; | |
4543 | asd->params.css_update_params_needed = true; | |
4544 | return 0; | |
4545 | } | |
4546 | ||
4547 | /* | |
4548 | * Function to configure bad pixel correction | |
4549 | */ | |
4550 | int atomisp_bad_pixel(struct atomisp_sub_device *asd, int flag, | |
4551 | __s32 *value) | |
4552 | { | |
4553 | ||
4554 | if (flag == 0) { | |
4555 | *value = asd->params.bad_pixel_en; | |
4556 | return 0; | |
4557 | } | |
4558 | asd->params.bad_pixel_en = !!*value; | |
4559 | ||
4560 | return 0; | |
4561 | } | |
4562 | ||
4563 | /* | |
4564 | * Function to configure bad pixel correction params | |
4565 | */ | |
4566 | int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, | |
4567 | struct atomisp_dp_config *config) | |
4568 | { | |
4569 | if (flag == 0) { | |
4570 | /* Get bad pixel from current setup */ | |
4571 | if (atomisp_css_get_dp_config(asd, config)) | |
4572 | return -EINVAL; | |
4573 | } else { | |
4574 | /* Set bad pixel to isp parameters */ | |
4575 | memcpy(&asd->params.css_param.dp_config, config, | |
4576 | sizeof(asd->params.css_param.dp_config)); | |
4577 | atomisp_css_set_dp_config(asd, &asd->params.css_param.dp_config); | |
4578 | asd->params.css_update_params_needed = true; | |
4579 | } | |
4580 | ||
4581 | return 0; | |
4582 | } | |
4583 | ||
4584 | /* | |
4585 | * Function to enable/disable video image stablization | |
4586 | */ | |
4587 | int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, | |
4588 | __s32 *value) | |
4589 | { | |
4590 | if (flag == 0) | |
4591 | *value = asd->params.video_dis_en; | |
4592 | else | |
4593 | asd->params.video_dis_en = !!*value; | |
4594 | ||
4595 | return 0; | |
4596 | } | |
4597 | ||
4598 | /* | |
4599 | * Function to configure fixed pattern noise | |
4600 | */ | |
4601 | int atomisp_fixed_pattern(struct atomisp_sub_device *asd, int flag, | |
4602 | __s32 *value) | |
4603 | { | |
4604 | ||
4605 | if (flag == 0) { | |
4606 | *value = asd->params.fpn_en; | |
4607 | return 0; | |
4608 | } | |
4609 | ||
4610 | if (*value == 0) { | |
4611 | asd->params.fpn_en = 0; | |
4612 | return 0; | |
4613 | } | |
4614 | ||
4615 | /* Add function to get black from from sensor with shutter off */ | |
4616 | return 0; | |
4617 | } | |
4618 | ||
4619 | static unsigned int | |
4620 | atomisp_bytesperline_to_padded_width(unsigned int bytesperline, | |
4621 | enum atomisp_css_frame_format format) | |
4622 | { | |
4623 | switch (format) { | |
4624 | case CSS_FRAME_FORMAT_UYVY: | |
4625 | case CSS_FRAME_FORMAT_YUYV: | |
4626 | case CSS_FRAME_FORMAT_RAW: | |
4627 | case CSS_FRAME_FORMAT_RGB565: | |
4628 | return bytesperline/2; | |
4629 | case CSS_FRAME_FORMAT_RGBA888: | |
4630 | return bytesperline/4; | |
4631 | /* The following cases could be removed, but we leave them | |
4632 | in to document the formats that are included. */ | |
4633 | case CSS_FRAME_FORMAT_NV11: | |
4634 | case CSS_FRAME_FORMAT_NV12: | |
4635 | case CSS_FRAME_FORMAT_NV16: | |
4636 | case CSS_FRAME_FORMAT_NV21: | |
4637 | case CSS_FRAME_FORMAT_NV61: | |
4638 | case CSS_FRAME_FORMAT_YV12: | |
4639 | case CSS_FRAME_FORMAT_YV16: | |
4640 | case CSS_FRAME_FORMAT_YUV420: | |
4641 | case CSS_FRAME_FORMAT_YUV420_16: | |
4642 | case CSS_FRAME_FORMAT_YUV422: | |
4643 | case CSS_FRAME_FORMAT_YUV422_16: | |
4644 | case CSS_FRAME_FORMAT_YUV444: | |
4645 | case CSS_FRAME_FORMAT_YUV_LINE: | |
4646 | case CSS_FRAME_FORMAT_PLANAR_RGB888: | |
4647 | case CSS_FRAME_FORMAT_QPLANE6: | |
4648 | case CSS_FRAME_FORMAT_BINARY_8: | |
4649 | default: | |
4650 | return bytesperline; | |
4651 | } | |
4652 | } | |
4653 | ||
4654 | static int | |
4655 | atomisp_v4l2_framebuffer_to_css_frame(const struct v4l2_framebuffer *arg, | |
4656 | struct atomisp_css_frame **result) | |
4657 | { | |
b5563094 | 4658 | struct atomisp_css_frame *res = NULL; |
a49d2536 AC |
4659 | unsigned int padded_width; |
4660 | enum atomisp_css_frame_format sh_format; | |
4661 | char *tmp_buf = NULL; | |
4662 | int ret = 0; | |
4663 | ||
4664 | sh_format = v4l2_fmt_to_sh_fmt(arg->fmt.pixelformat); | |
4665 | padded_width = atomisp_bytesperline_to_padded_width( | |
4666 | arg->fmt.bytesperline, sh_format); | |
4667 | ||
4668 | /* Note: the padded width on an atomisp_css_frame is in elements, not in | |
4669 | bytes. The RAW frame we use here should always be a 16bit RAW | |
4670 | frame. This is why we bytesperline/2 is equal to the padded with */ | |
4671 | if (atomisp_css_frame_allocate(&res, arg->fmt.width, arg->fmt.height, | |
4672 | sh_format, padded_width, 0)) { | |
4673 | ret = -ENOMEM; | |
4674 | goto err; | |
4675 | } | |
4676 | ||
4677 | tmp_buf = vmalloc(arg->fmt.sizeimage); | |
4678 | if (!tmp_buf) { | |
4679 | ret = -ENOMEM; | |
4680 | goto err; | |
4681 | } | |
4682 | if (copy_from_user(tmp_buf, (void __user __force *)arg->base, | |
4683 | arg->fmt.sizeimage)) { | |
4684 | ret = -EFAULT; | |
4685 | goto err; | |
4686 | } | |
4687 | ||
4688 | if (hmm_store(res->data, tmp_buf, arg->fmt.sizeimage)) { | |
4689 | ret = -EINVAL; | |
4690 | goto err; | |
4691 | } | |
4692 | ||
4693 | err: | |
4694 | if (ret && res) | |
4695 | atomisp_css_frame_free(res); | |
4696 | if (tmp_buf) | |
4697 | vfree(tmp_buf); | |
4698 | if (ret == 0) | |
4699 | *result = res; | |
4700 | return ret; | |
4701 | } | |
4702 | ||
4703 | /* | |
4704 | * Function to configure fixed pattern noise table | |
4705 | */ | |
4706 | int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, | |
4707 | struct v4l2_framebuffer *arg) | |
4708 | { | |
4709 | struct atomisp_css_frame *raw_black_frame = NULL; | |
4710 | int ret; | |
4711 | ||
4712 | if (arg == NULL) | |
4713 | return -EINVAL; | |
4714 | ||
4715 | ret = atomisp_v4l2_framebuffer_to_css_frame(arg, &raw_black_frame); | |
4716 | if (ret) | |
4717 | return ret; | |
4718 | if (atomisp_css_set_black_frame(asd, raw_black_frame)) | |
4719 | ret = -ENOMEM; | |
4720 | ||
4721 | atomisp_css_frame_free(raw_black_frame); | |
4722 | return ret; | |
4723 | } | |
4724 | ||
4725 | /* | |
4726 | * Function to configure false color correction | |
4727 | */ | |
4728 | int atomisp_false_color(struct atomisp_sub_device *asd, int flag, | |
4729 | __s32 *value) | |
4730 | { | |
4731 | /* Get nr config from current setup */ | |
4732 | if (flag == 0) { | |
4733 | *value = asd->params.false_color; | |
4734 | return 0; | |
4735 | } | |
4736 | ||
4737 | /* Set nr config to isp parameters */ | |
4738 | if (*value) { | |
4739 | atomisp_css_set_default_de_config(asd); | |
4740 | } else { | |
4741 | asd->params.css_param.de_config.pixelnoise = 0; | |
4742 | atomisp_css_set_de_config(asd, &asd->params.css_param.de_config); | |
4743 | } | |
4744 | asd->params.css_update_params_needed = true; | |
4745 | asd->params.false_color = *value; | |
4746 | return 0; | |
4747 | } | |
4748 | ||
4749 | /* | |
4750 | * Function to configure bad pixel correction params | |
4751 | */ | |
4752 | int atomisp_false_color_param(struct atomisp_sub_device *asd, int flag, | |
4753 | struct atomisp_de_config *config) | |
4754 | { | |
4755 | if (flag == 0) { | |
4756 | /* Get false color from current setup */ | |
4757 | if (atomisp_css_get_de_config(asd, config)) | |
4758 | return -EINVAL; | |
4759 | } else { | |
4760 | /* Set false color to isp parameters */ | |
4761 | memcpy(&asd->params.css_param.de_config, config, | |
4995706d | 4762 | sizeof(asd->params.css_param.de_config)); |
a49d2536 AC |
4763 | atomisp_css_set_de_config(asd, &asd->params.css_param.de_config); |
4764 | asd->params.css_update_params_needed = true; | |
4765 | } | |
4766 | ||
4767 | return 0; | |
4768 | } | |
4769 | ||
4770 | /* | |
4771 | * Function to configure white balance params | |
4772 | */ | |
4773 | int atomisp_white_balance_param(struct atomisp_sub_device *asd, int flag, | |
4774 | struct atomisp_wb_config *config) | |
4775 | { | |
4776 | if (flag == 0) { | |
4777 | /* Get white balance from current setup */ | |
4778 | if (atomisp_css_get_wb_config(asd, config)) | |
4779 | return -EINVAL; | |
4780 | } else { | |
4781 | /* Set white balance to isp parameters */ | |
4782 | memcpy(&asd->params.css_param.wb_config, config, | |
4995706d | 4783 | sizeof(asd->params.css_param.wb_config)); |
a49d2536 AC |
4784 | atomisp_css_set_wb_config(asd, &asd->params.css_param.wb_config); |
4785 | asd->params.css_update_params_needed = true; | |
4786 | } | |
4787 | ||
4788 | return 0; | |
4789 | } | |
4790 | ||
4791 | int atomisp_3a_config_param(struct atomisp_sub_device *asd, int flag, | |
4792 | struct atomisp_3a_config *config) | |
4793 | { | |
4794 | struct atomisp_device *isp = asd->isp; | |
4795 | ||
4796 | dev_dbg(isp->dev, ">%s %d\n", __func__, flag); | |
4797 | ||
4798 | if (flag == 0) { | |
4799 | /* Get white balance from current setup */ | |
4800 | if (atomisp_css_get_3a_config(asd, config)) | |
4801 | return -EINVAL; | |
4802 | } else { | |
4803 | /* Set white balance to isp parameters */ | |
4804 | memcpy(&asd->params.css_param.s3a_config, config, | |
4995706d | 4805 | sizeof(asd->params.css_param.s3a_config)); |
a49d2536 AC |
4806 | atomisp_css_set_3a_config(asd, &asd->params.css_param.s3a_config); |
4807 | asd->params.css_update_params_needed = true; | |
4808 | } | |
4809 | ||
4810 | dev_dbg(isp->dev, "<%s %d\n", __func__, flag); | |
4811 | return 0; | |
4812 | } | |
4813 | ||
4814 | /* | |
4815 | * Function to setup digital zoom | |
4816 | */ | |
4817 | int atomisp_digital_zoom(struct atomisp_sub_device *asd, int flag, | |
4818 | __s32 *value) | |
4819 | { | |
4820 | u32 zoom; | |
4821 | struct atomisp_device *isp = asd->isp; | |
4822 | ||
4823 | unsigned int max_zoom = MRFLD_MAX_ZOOM_FACTOR; | |
4824 | ||
4825 | if (flag == 0) { | |
4826 | atomisp_css_get_zoom_factor(asd, &zoom); | |
4827 | *value = max_zoom - zoom; | |
4828 | } else { | |
4829 | if (*value < 0) | |
4830 | return -EINVAL; | |
4831 | ||
4832 | zoom = max_zoom - min_t(u32, max_zoom - 1, *value); | |
4833 | atomisp_css_set_zoom_factor(asd, zoom); | |
4834 | ||
4835 | dev_dbg(isp->dev, "%s, zoom: %d\n", __func__, zoom); | |
4836 | asd->params.css_update_params_needed = true; | |
4837 | } | |
4838 | ||
4839 | return 0; | |
4840 | } | |
4841 | ||
4842 | /* | |
4843 | * Function to get sensor specific info for current resolution, | |
4844 | * which will be used for auto exposure conversion. | |
4845 | */ | |
4846 | int atomisp_get_sensor_mode_data(struct atomisp_sub_device *asd, | |
4847 | struct atomisp_sensor_mode_data *config) | |
4848 | { | |
4849 | struct camera_mipi_info *mipi_info; | |
4850 | struct atomisp_device *isp = asd->isp; | |
4851 | ||
4852 | mipi_info = atomisp_to_sensor_mipi_info( | |
4853 | isp->inputs[asd->input_curr].camera); | |
4854 | if (mipi_info == NULL) | |
4855 | return -EINVAL; | |
4856 | ||
4857 | memcpy(config, &mipi_info->data, sizeof(*config)); | |
4858 | return 0; | |
4859 | } | |
4860 | ||
4861 | int atomisp_get_fmt(struct video_device *vdev, struct v4l2_format *f) | |
4862 | { | |
4863 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
4864 | ||
4865 | f->fmt.pix = pipe->pix; | |
4866 | ||
4867 | return 0; | |
4868 | } | |
4869 | ||
4870 | static void __atomisp_update_stream_env(struct atomisp_sub_device *asd, | |
4871 | uint16_t stream_index, struct atomisp_input_stream_info *stream_info) | |
4872 | { | |
4873 | int i; | |
4874 | ||
4875 | #if defined(ISP2401_NEW_INPUT_SYSTEM) | |
4876 | /* assign virtual channel id return from sensor driver query */ | |
4877 | asd->stream_env[stream_index].ch_id = stream_info->ch_id; | |
4878 | #endif | |
4879 | asd->stream_env[stream_index].isys_configs = stream_info->isys_configs; | |
4880 | for (i = 0; i < stream_info->isys_configs; i++) { | |
4881 | asd->stream_env[stream_index].isys_info[i].input_format = | |
4882 | stream_info->isys_info[i].input_format; | |
4883 | asd->stream_env[stream_index].isys_info[i].width = | |
4884 | stream_info->isys_info[i].width; | |
4885 | asd->stream_env[stream_index].isys_info[i].height = | |
4886 | stream_info->isys_info[i].height; | |
4887 | } | |
4888 | } | |
4889 | ||
4890 | static void __atomisp_init_stream_info(uint16_t stream_index, | |
4891 | struct atomisp_input_stream_info *stream_info) | |
4892 | { | |
4893 | int i; | |
4894 | ||
4895 | stream_info->enable = 1; | |
4896 | stream_info->stream = stream_index; | |
4897 | stream_info->ch_id = 0; | |
4898 | stream_info->isys_configs = 0; | |
4899 | for (i = 0; i < MAX_STREAMS_PER_CHANNEL; i++) { | |
4900 | stream_info->isys_info[i].input_format = 0; | |
4901 | stream_info->isys_info[i].width = 0; | |
4902 | stream_info->isys_info[i].height = 0; | |
4903 | } | |
4904 | } | |
4905 | ||
4906 | /* This function looks up the closest available resolution. */ | |
4907 | int atomisp_try_fmt(struct video_device *vdev, struct v4l2_format *f, | |
4908 | bool *res_overflow) | |
4909 | { | |
4910 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
4911 | struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; | |
a49d2536 | 4912 | struct v4l2_subdev_pad_config pad_cfg; |
a49d2536 AC |
4913 | struct v4l2_subdev_format format = { |
4914 | .which = V4L2_SUBDEV_FORMAT_TRY, | |
a49d2536 | 4915 | }; |
cc4e33d9 | 4916 | |
a49d2536 AC |
4917 | struct v4l2_mbus_framefmt *snr_mbus_fmt = &format.format; |
4918 | const struct atomisp_format_bridge *fmt; | |
4919 | struct atomisp_input_stream_info *stream_info = | |
a49d2536 | 4920 | (struct atomisp_input_stream_info *)snr_mbus_fmt->reserved; |
a49d2536 AC |
4921 | uint16_t stream_index; |
4922 | int source_pad = atomisp_subdev_source_pad(vdev); | |
4923 | int ret; | |
4924 | ||
4925 | if (isp->inputs[asd->input_curr].camera == NULL) | |
4926 | return -EINVAL; | |
4927 | ||
4928 | stream_index = atomisp_source_pad_to_stream_id(asd, source_pad); | |
4929 | fmt = atomisp_get_format_bridge(f->fmt.pix.pixelformat); | |
4930 | if (fmt == NULL) { | |
4931 | dev_err(isp->dev, "unsupported pixelformat!\n"); | |
4932 | fmt = atomisp_output_fmts; | |
4933 | } | |
4934 | ||
4935 | #ifdef ISP2401 | |
4936 | if (f->fmt.pix.width <= 0 || f->fmt.pix.height <= 0) | |
4937 | return -EINVAL; | |
4938 | ||
4939 | #endif | |
4940 | snr_mbus_fmt->code = fmt->mbus_code; | |
4941 | snr_mbus_fmt->width = f->fmt.pix.width; | |
4942 | snr_mbus_fmt->height = f->fmt.pix.height; | |
4943 | ||
4944 | __atomisp_init_stream_info(stream_index, stream_info); | |
4945 | ||
4946 | dev_dbg(isp->dev, "try_mbus_fmt: asking for %ux%u\n", | |
4947 | snr_mbus_fmt->width, snr_mbus_fmt->height); | |
4948 | ||
4949 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
a49d2536 | 4950 | pad, set_fmt, &pad_cfg, &format); |
a49d2536 AC |
4951 | if (ret) |
4952 | return ret; | |
4953 | ||
4954 | dev_dbg(isp->dev, "try_mbus_fmt: got %ux%u\n", | |
4955 | snr_mbus_fmt->width, snr_mbus_fmt->height); | |
4956 | ||
4957 | fmt = atomisp_get_format_bridge_from_mbus(snr_mbus_fmt->code); | |
4958 | if (fmt == NULL) { | |
4959 | dev_err(isp->dev, "unknown sensor format 0x%8.8x\n", | |
4960 | snr_mbus_fmt->code); | |
4961 | return -EINVAL; | |
4962 | } | |
4963 | ||
4964 | f->fmt.pix.pixelformat = fmt->pixelformat; | |
4965 | ||
4966 | /* | |
4967 | * If the format is jpeg or custom RAW, then the width and height will | |
4968 | * not satisfy the normal atomisp requirements and no need to check | |
4969 | * the below conditions. So just assign to what is being returned from | |
4970 | * the sensor driver. | |
4971 | */ | |
4972 | if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG || | |
4973 | f->fmt.pix.pixelformat == V4L2_PIX_FMT_CUSTOM_M10MO_RAW) { | |
4974 | f->fmt.pix.width = snr_mbus_fmt->width; | |
4975 | f->fmt.pix.height = snr_mbus_fmt->height; | |
4976 | return 0; | |
4977 | } | |
4978 | ||
4979 | if (snr_mbus_fmt->width < f->fmt.pix.width | |
4980 | && snr_mbus_fmt->height < f->fmt.pix.height) { | |
4981 | f->fmt.pix.width = snr_mbus_fmt->width; | |
4982 | f->fmt.pix.height = snr_mbus_fmt->height; | |
4983 | /* Set the flag when resolution requested is | |
4984 | * beyond the max value supported by sensor | |
4985 | */ | |
4986 | if (res_overflow != NULL) | |
4987 | *res_overflow = true; | |
4988 | } | |
4989 | ||
4990 | /* app vs isp */ | |
4991 | f->fmt.pix.width = rounddown( | |
4992 | clamp_t(u32, f->fmt.pix.width, ATOM_ISP_MIN_WIDTH, | |
4993 | ATOM_ISP_MAX_WIDTH), ATOM_ISP_STEP_WIDTH); | |
4994 | f->fmt.pix.height = rounddown( | |
4995 | clamp_t(u32, f->fmt.pix.height, ATOM_ISP_MIN_HEIGHT, | |
4996 | ATOM_ISP_MAX_HEIGHT), ATOM_ISP_STEP_HEIGHT); | |
4997 | ||
4998 | return 0; | |
4999 | } | |
5000 | ||
5001 | static int | |
5002 | atomisp_try_fmt_file(struct atomisp_device *isp, struct v4l2_format *f) | |
5003 | { | |
5004 | u32 width = f->fmt.pix.width; | |
5005 | u32 height = f->fmt.pix.height; | |
5006 | u32 pixelformat = f->fmt.pix.pixelformat; | |
5007 | enum v4l2_field field = f->fmt.pix.field; | |
5008 | u32 depth; | |
5009 | ||
5010 | if (!atomisp_get_format_bridge(pixelformat)) { | |
5011 | dev_err(isp->dev, "Wrong output pixelformat\n"); | |
5012 | return -EINVAL; | |
5013 | } | |
5014 | ||
5015 | depth = get_pixel_depth(pixelformat); | |
5016 | ||
02838938 | 5017 | if (field == V4L2_FIELD_ANY) |
a49d2536 AC |
5018 | field = V4L2_FIELD_NONE; |
5019 | else if (field != V4L2_FIELD_NONE) { | |
5020 | dev_err(isp->dev, "Wrong output field\n"); | |
5021 | return -EINVAL; | |
5022 | } | |
5023 | ||
5024 | f->fmt.pix.field = field; | |
5025 | f->fmt.pix.width = clamp_t(u32, | |
5026 | rounddown(width, (u32)ATOM_ISP_STEP_WIDTH), | |
5027 | ATOM_ISP_MIN_WIDTH, ATOM_ISP_MAX_WIDTH); | |
5028 | f->fmt.pix.height = clamp_t(u32, rounddown(height, | |
4995706d | 5029 | (u32)ATOM_ISP_STEP_HEIGHT), |
a49d2536 AC |
5030 | ATOM_ISP_MIN_HEIGHT, ATOM_ISP_MAX_HEIGHT); |
5031 | f->fmt.pix.bytesperline = (width * depth) >> 3; | |
5032 | ||
5033 | return 0; | |
5034 | } | |
5035 | ||
5036 | mipi_port_ID_t __get_mipi_port(struct atomisp_device *isp, | |
5037 | enum atomisp_camera_port port) | |
5038 | { | |
5039 | switch (port) { | |
5040 | case ATOMISP_CAMERA_PORT_PRIMARY: | |
5041 | return MIPI_PORT0_ID; | |
5042 | case ATOMISP_CAMERA_PORT_SECONDARY: | |
5043 | return MIPI_PORT1_ID; | |
5044 | case ATOMISP_CAMERA_PORT_TERTIARY: | |
5045 | if (MIPI_PORT1_ID + 1 != N_MIPI_PORT_ID) | |
5046 | return MIPI_PORT1_ID + 1; | |
5047 | /* go through down for else case */ | |
5048 | default: | |
5049 | dev_err(isp->dev, "unsupported port: %d\n", port); | |
5050 | return MIPI_PORT0_ID; | |
5051 | } | |
5052 | } | |
5053 | ||
5054 | static inline int atomisp_set_sensor_mipi_to_isp( | |
5055 | struct atomisp_sub_device *asd, | |
5056 | enum atomisp_input_stream_id stream_id, | |
5057 | struct camera_mipi_info *mipi_info) | |
5058 | { | |
5059 | struct v4l2_control ctrl; | |
5060 | struct atomisp_device *isp = asd->isp; | |
5061 | const struct atomisp_in_fmt_conv *fc; | |
5062 | int mipi_freq = 0; | |
5063 | unsigned int input_format, bayer_order; | |
5064 | ||
5065 | ctrl.id = V4L2_CID_LINK_FREQ; | |
5066 | if (v4l2_g_ctrl | |
5067 | (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl) == 0) | |
5068 | mipi_freq = ctrl.value; | |
5069 | ||
5070 | if (asd->stream_env[stream_id].isys_configs == 1) { | |
5071 | input_format = | |
5072 | asd->stream_env[stream_id].isys_info[0].input_format; | |
5073 | atomisp_css_isys_set_format(asd, stream_id, | |
5074 | input_format, IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX); | |
5075 | } else if (asd->stream_env[stream_id].isys_configs == 2) { | |
5076 | atomisp_css_isys_two_stream_cfg_update_stream1( | |
5077 | asd, stream_id, | |
5078 | asd->stream_env[stream_id].isys_info[0].input_format, | |
5079 | asd->stream_env[stream_id].isys_info[0].width, | |
5080 | asd->stream_env[stream_id].isys_info[0].height); | |
5081 | ||
5082 | atomisp_css_isys_two_stream_cfg_update_stream2( | |
5083 | asd, stream_id, | |
5084 | asd->stream_env[stream_id].isys_info[1].input_format, | |
5085 | asd->stream_env[stream_id].isys_info[1].width, | |
5086 | asd->stream_env[stream_id].isys_info[1].height); | |
5087 | } | |
5088 | ||
5089 | /* Compatibility for sensors which provide no media bus code | |
5090 | * in s_mbus_framefmt() nor support pad formats. */ | |
5091 | if (mipi_info->input_format != -1) { | |
5092 | bayer_order = mipi_info->raw_bayer_order; | |
5093 | ||
5094 | /* Input stream config is still needs configured */ | |
5095 | /* TODO: Check if this is necessary */ | |
5096 | fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt( | |
5097 | mipi_info->input_format); | |
5098 | if (!fc) | |
5099 | return -EINVAL; | |
5100 | input_format = fc->css_stream_fmt; | |
5101 | } else { | |
5102 | struct v4l2_mbus_framefmt *sink; | |
5103 | sink = atomisp_subdev_get_ffmt(&asd->subdev, NULL, | |
4995706d DY |
5104 | V4L2_SUBDEV_FORMAT_ACTIVE, |
5105 | ATOMISP_SUBDEV_PAD_SINK); | |
a49d2536 AC |
5106 | fc = atomisp_find_in_fmt_conv(sink->code); |
5107 | if (!fc) | |
5108 | return -EINVAL; | |
5109 | input_format = fc->css_stream_fmt; | |
5110 | bayer_order = fc->bayer_order; | |
5111 | } | |
5112 | ||
5113 | atomisp_css_input_set_format(asd, stream_id, input_format); | |
5114 | atomisp_css_input_set_bayer_order(asd, stream_id, bayer_order); | |
5115 | ||
5116 | fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt( | |
5117 | mipi_info->metadata_format); | |
5118 | if (!fc) | |
5119 | return -EINVAL; | |
5120 | input_format = fc->css_stream_fmt; | |
5121 | atomisp_css_input_configure_port(asd, | |
5122 | __get_mipi_port(asd->isp, mipi_info->port), | |
5123 | mipi_info->num_lanes, | |
5124 | 0xffff4, mipi_freq, | |
5125 | input_format, | |
5126 | mipi_info->metadata_width, | |
5127 | mipi_info->metadata_height); | |
5128 | return 0; | |
5129 | } | |
5130 | ||
5131 | static int __enable_continuous_mode(struct atomisp_sub_device *asd, | |
5132 | bool enable) | |
5133 | { | |
5134 | struct atomisp_device *isp = asd->isp; | |
5135 | ||
5136 | dev_dbg(isp->dev, | |
5137 | "continuous mode %d, raw buffers %d, stop preview %d\n", | |
5138 | enable, asd->continuous_raw_buffer_size->val, | |
5139 | !asd->continuous_viewfinder->val); | |
5140 | #ifndef ISP2401 | |
5141 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_PRIMARY); | |
5142 | #else | |
5143 | atomisp_update_capture_mode(asd); | |
5144 | #endif | |
5145 | /* in case of ANR, force capture pipe to offline mode */ | |
5146 | atomisp_css_capture_enable_online(asd, ATOMISP_INPUT_STREAM_GENERAL, | |
5147 | asd->params.low_light ? false : !enable); | |
5148 | atomisp_css_preview_enable_online(asd, ATOMISP_INPUT_STREAM_GENERAL, | |
5149 | !enable); | |
5150 | atomisp_css_enable_continuous(asd, enable); | |
5151 | atomisp_css_enable_cvf(asd, asd->continuous_viewfinder->val); | |
5152 | ||
5153 | if (atomisp_css_continuous_set_num_raw_frames(asd, | |
5154 | asd->continuous_raw_buffer_size->val)) { | |
5155 | dev_err(isp->dev, "css_continuous_set_num_raw_frames failed\n"); | |
5156 | return -EINVAL; | |
5157 | } | |
5158 | ||
5159 | if (!enable) { | |
5160 | atomisp_css_enable_raw_binning(asd, false); | |
5161 | atomisp_css_input_set_two_pixels_per_clock(asd, false); | |
5162 | } | |
5163 | ||
5164 | if (isp->inputs[asd->input_curr].type != FILE_INPUT) | |
5165 | atomisp_css_input_set_mode(asd, CSS_INPUT_MODE_SENSOR); | |
5166 | ||
5167 | return atomisp_update_run_mode(asd); | |
5168 | } | |
5169 | ||
5170 | int configure_pp_input_nop(struct atomisp_sub_device *asd, | |
5171 | unsigned int width, unsigned int height) | |
5172 | { | |
5173 | return 0; | |
5174 | } | |
5175 | ||
5176 | int configure_output_nop(struct atomisp_sub_device *asd, | |
4995706d DY |
5177 | unsigned int width, unsigned int height, |
5178 | unsigned int min_width, | |
5179 | enum atomisp_css_frame_format sh_fmt) | |
a49d2536 AC |
5180 | { |
5181 | return 0; | |
5182 | } | |
5183 | ||
5184 | int get_frame_info_nop(struct atomisp_sub_device *asd, | |
4995706d | 5185 | struct atomisp_css_frame_info *finfo) |
a49d2536 AC |
5186 | { |
5187 | return 0; | |
5188 | } | |
5189 | ||
d929fb4e | 5190 | /* |
a49d2536 AC |
5191 | * Resets CSS parameters that depend on input resolution. |
5192 | * | |
5193 | * Update params like CSS RAW binning, 2ppc mode and pp_input | |
5194 | * which depend on input size, but are not automatically | |
5195 | * handled in CSS when the input resolution is changed. | |
5196 | */ | |
5197 | static int css_input_resolution_changed(struct atomisp_sub_device *asd, | |
5198 | struct v4l2_mbus_framefmt *ffmt) | |
5199 | { | |
5200 | struct atomisp_metadata_buf *md_buf = NULL, *_md_buf; | |
5201 | unsigned int i; | |
5202 | ||
5203 | dev_dbg(asd->isp->dev, "css_input_resolution_changed to %ux%u\n", | |
5204 | ffmt->width, ffmt->height); | |
5205 | ||
5206 | #if defined(ISP2401_NEW_INPUT_SYSTEM) | |
5207 | atomisp_css_input_set_two_pixels_per_clock(asd, false); | |
5208 | #else | |
5209 | atomisp_css_input_set_two_pixels_per_clock(asd, true); | |
5210 | #endif | |
5211 | if (asd->continuous_mode->val) { | |
5212 | /* Note for all checks: ffmt includes pad_w+pad_h */ | |
5213 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO || | |
5214 | (ffmt->width >= 2048 || ffmt->height >= 1536)) { | |
5215 | /* | |
5216 | * For preview pipe, enable only if resolution | |
5217 | * is >= 3M for ISP2400. | |
5218 | */ | |
5219 | atomisp_css_enable_raw_binning(asd, true); | |
5220 | } | |
5221 | } | |
5222 | /* | |
5223 | * If sensor input changed, which means metadata resolution changed | |
5224 | * together. Release all metadata buffers here to let it re-allocated | |
5225 | * next time in reqbufs. | |
5226 | */ | |
5227 | for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) { | |
5228 | list_for_each_entry_safe(md_buf, _md_buf, &asd->metadata[i], | |
4995706d | 5229 | list) { |
a49d2536 AC |
5230 | atomisp_css_free_metadata_buffer(md_buf); |
5231 | list_del(&md_buf->list); | |
5232 | kfree(md_buf); | |
5233 | } | |
5234 | } | |
5235 | return 0; | |
5236 | ||
5237 | /* | |
5238 | * TODO: atomisp_css_preview_configure_pp_input() not | |
5239 | * reset due to CSS bug tracked as PSI BZ 115124 | |
5240 | */ | |
5241 | } | |
5242 | ||
5243 | static int atomisp_set_fmt_to_isp(struct video_device *vdev, | |
5244 | struct atomisp_css_frame_info *output_info, | |
5245 | struct atomisp_css_frame_info *raw_output_info, | |
5246 | struct v4l2_pix_format *pix, | |
5247 | unsigned int source_pad) | |
5248 | { | |
5249 | struct camera_mipi_info *mipi_info; | |
5250 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
5251 | struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; | |
5252 | const struct atomisp_format_bridge *format; | |
5253 | struct v4l2_rect *isp_sink_crop; | |
5254 | enum atomisp_css_pipe_id pipe_id; | |
5255 | struct v4l2_subdev_fh fh; | |
5256 | int (*configure_output)(struct atomisp_sub_device *asd, | |
5257 | unsigned int width, unsigned int height, | |
5258 | unsigned int min_width, | |
5259 | enum atomisp_css_frame_format sh_fmt) = | |
5260 | configure_output_nop; | |
5261 | int (*get_frame_info)(struct atomisp_sub_device *asd, | |
4995706d | 5262 | struct atomisp_css_frame_info *finfo) = |
a49d2536 AC |
5263 | get_frame_info_nop; |
5264 | int (*configure_pp_input)(struct atomisp_sub_device *asd, | |
5265 | unsigned int width, unsigned int height) = | |
5266 | configure_pp_input_nop; | |
5267 | uint16_t stream_index = atomisp_source_pad_to_stream_id(asd, source_pad); | |
5268 | const struct atomisp_in_fmt_conv *fc; | |
5269 | int ret; | |
5270 | ||
5271 | v4l2_fh_init(&fh.vfh, vdev); | |
5272 | ||
5273 | isp_sink_crop = atomisp_subdev_get_rect( | |
5274 | &asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, | |
5275 | ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); | |
5276 | ||
5277 | format = atomisp_get_format_bridge(pix->pixelformat); | |
5278 | if (format == NULL) | |
5279 | return -EINVAL; | |
5280 | ||
5281 | if (isp->inputs[asd->input_curr].type != TEST_PATTERN && | |
5282 | isp->inputs[asd->input_curr].type != FILE_INPUT) { | |
5283 | mipi_info = atomisp_to_sensor_mipi_info( | |
5284 | isp->inputs[asd->input_curr].camera); | |
5285 | if (!mipi_info) { | |
5286 | dev_err(isp->dev, "mipi_info is NULL\n"); | |
5287 | return -EINVAL; | |
5288 | } | |
5289 | if (atomisp_set_sensor_mipi_to_isp(asd, stream_index, | |
5290 | mipi_info)) | |
5291 | return -EINVAL; | |
5292 | fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt( | |
5293 | mipi_info->input_format); | |
5294 | if (!fc) | |
5295 | fc = atomisp_find_in_fmt_conv( | |
5296 | atomisp_subdev_get_ffmt(&asd->subdev, | |
5297 | NULL, V4L2_SUBDEV_FORMAT_ACTIVE, | |
5298 | ATOMISP_SUBDEV_PAD_SINK)->code); | |
5299 | if (!fc) | |
5300 | return -EINVAL; | |
5301 | if (format->sh_fmt == CSS_FRAME_FORMAT_RAW && | |
5302 | raw_output_format_match_input(fc->css_stream_fmt, | |
5303 | pix->pixelformat)) | |
5304 | return -EINVAL; | |
5305 | } | |
5306 | ||
5307 | /* | |
5308 | * Configure viewfinder also when vfpp is disabled: the | |
5309 | * CSS still requires viewfinder configuration. | |
5310 | */ | |
5311 | if (asd->fmt_auto->val || | |
5312 | asd->vfpp->val != ATOMISP_VFPP_ENABLE) { | |
5313 | struct v4l2_rect vf_size = {0}; | |
5314 | struct v4l2_mbus_framefmt vf_ffmt = {0}; | |
5315 | ||
5316 | if (pix->width < 640 || pix->height < 480) { | |
5317 | vf_size.width = pix->width; | |
5318 | vf_size.height = pix->height; | |
5319 | } else { | |
5320 | vf_size.width = 640; | |
5321 | vf_size.height = 480; | |
5322 | } | |
5323 | ||
5324 | /* FIXME: proper format name for this one. See | |
5325 | atomisp_output_fmts[] in atomisp_v4l2.c */ | |
5326 | vf_ffmt.code = V4L2_MBUS_FMT_CUSTOM_YUV420; | |
5327 | ||
5328 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
5329 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5330 | ATOMISP_SUBDEV_PAD_SOURCE_VF, | |
5331 | V4L2_SEL_TGT_COMPOSE, 0, &vf_size); | |
5332 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, | |
5333 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5334 | ATOMISP_SUBDEV_PAD_SOURCE_VF, &vf_ffmt); | |
5335 | asd->video_out_vf.sh_fmt = CSS_FRAME_FORMAT_NV12; | |
5336 | ||
5337 | if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { | |
5338 | atomisp_css_video_configure_viewfinder(asd, | |
5339 | vf_size.width, vf_size.height, 0, | |
5340 | asd->video_out_vf.sh_fmt); | |
5341 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
5342 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW || | |
5343 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) | |
5344 | atomisp_css_video_configure_viewfinder(asd, | |
5345 | vf_size.width, vf_size.height, 0, | |
5346 | asd->video_out_vf.sh_fmt); | |
5347 | else | |
5348 | atomisp_css_capture_configure_viewfinder(asd, | |
5349 | vf_size.width, vf_size.height, 0, | |
5350 | asd->video_out_vf.sh_fmt); | |
5351 | } else if (source_pad != ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW || | |
5352 | asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { | |
5353 | atomisp_css_capture_configure_viewfinder(asd, | |
5354 | vf_size.width, vf_size.height, 0, | |
5355 | asd->video_out_vf.sh_fmt); | |
5356 | } | |
5357 | } | |
5358 | ||
5359 | if (asd->continuous_mode->val) { | |
5360 | ret = __enable_continuous_mode(asd, true); | |
5361 | if (ret) | |
5362 | return -EINVAL; | |
5363 | } | |
5364 | ||
5365 | atomisp_css_input_set_mode(asd, CSS_INPUT_MODE_SENSOR); | |
5366 | atomisp_css_disable_vf_pp(asd, | |
5367 | asd->vfpp->val != ATOMISP_VFPP_ENABLE); | |
5368 | ||
5369 | /* ISP2401 new input system need to use copy pipe */ | |
5370 | if (asd->copy_mode) { | |
5371 | pipe_id = CSS_PIPE_ID_COPY; | |
5372 | atomisp_css_capture_enable_online(asd, stream_index, false); | |
5373 | } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { | |
5374 | /* video same in continuouscapture and online modes */ | |
5375 | configure_output = atomisp_css_video_configure_output; | |
5376 | get_frame_info = atomisp_css_video_get_output_frame_info; | |
5377 | pipe_id = CSS_PIPE_ID_VIDEO; | |
5378 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
5379 | if (!asd->continuous_mode->val) { | |
5380 | configure_output = atomisp_css_video_configure_output; | |
5381 | get_frame_info = | |
5382 | atomisp_css_video_get_output_frame_info; | |
5383 | pipe_id = CSS_PIPE_ID_VIDEO; | |
5384 | } else { | |
5385 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW || | |
5386 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) { | |
5387 | configure_output = | |
5388 | atomisp_css_video_configure_output; | |
5389 | get_frame_info = | |
5390 | atomisp_css_video_get_output_frame_info; | |
5391 | configure_pp_input = | |
5392 | atomisp_css_video_configure_pp_input; | |
5393 | pipe_id = CSS_PIPE_ID_VIDEO; | |
5394 | } else { | |
5395 | configure_output = | |
5396 | atomisp_css_capture_configure_output; | |
5397 | get_frame_info = | |
5398 | atomisp_css_capture_get_output_frame_info; | |
5399 | configure_pp_input = | |
5400 | atomisp_css_capture_configure_pp_input; | |
5401 | pipe_id = CSS_PIPE_ID_CAPTURE; | |
5402 | ||
5403 | atomisp_update_capture_mode(asd); | |
5404 | atomisp_css_capture_enable_online(asd, stream_index, false); | |
5405 | } | |
5406 | } | |
5407 | } else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) { | |
5408 | configure_output = atomisp_css_preview_configure_output; | |
5409 | get_frame_info = atomisp_css_preview_get_output_frame_info; | |
5410 | configure_pp_input = atomisp_css_preview_configure_pp_input; | |
5411 | pipe_id = CSS_PIPE_ID_PREVIEW; | |
5412 | } else { | |
5413 | /* CSS doesn't support low light mode on SOC cameras, so disable | |
5414 | * it. FIXME: if this is done elsewhere, it gives corrupted | |
5415 | * colors into thumbnail image. | |
5416 | */ | |
5417 | if (isp->inputs[asd->input_curr].type == SOC_CAMERA) | |
5418 | asd->params.low_light = false; | |
5419 | ||
5420 | if (format->sh_fmt == CSS_FRAME_FORMAT_RAW) { | |
5421 | atomisp_css_capture_set_mode(asd, CSS_CAPTURE_MODE_RAW); | |
5422 | atomisp_css_enable_dz(asd, false); | |
5423 | } else { | |
5424 | atomisp_update_capture_mode(asd); | |
5425 | } | |
5426 | ||
5427 | if (!asd->continuous_mode->val) | |
5428 | /* in case of ANR, force capture pipe to offline mode */ | |
5429 | atomisp_css_capture_enable_online(asd, stream_index, | |
5430 | asd->params.low_light ? | |
5431 | false : asd->params.online_process); | |
5432 | ||
5433 | configure_output = atomisp_css_capture_configure_output; | |
5434 | get_frame_info = atomisp_css_capture_get_output_frame_info; | |
5435 | configure_pp_input = atomisp_css_capture_configure_pp_input; | |
5436 | pipe_id = CSS_PIPE_ID_CAPTURE; | |
5437 | ||
5438 | if (!asd->params.online_process && | |
5439 | !asd->continuous_mode->val) { | |
5440 | ret = atomisp_css_capture_get_output_raw_frame_info(asd, | |
5441 | raw_output_info); | |
5442 | if (ret) | |
5443 | return ret; | |
5444 | } | |
5445 | if (!asd->continuous_mode->val && asd->run_mode->val | |
5446 | != ATOMISP_RUN_MODE_STILL_CAPTURE) { | |
5447 | dev_err(isp->dev, | |
5448 | "Need to set the running mode first\n"); | |
5449 | asd->run_mode->val = ATOMISP_RUN_MODE_STILL_CAPTURE; | |
5450 | } | |
5451 | } | |
5452 | ||
5453 | /* | |
5454 | * to SOC camera, use yuvpp pipe. | |
5455 | */ | |
5456 | if (ATOMISP_USE_YUVPP(asd)) | |
5457 | pipe_id = CSS_PIPE_ID_YUVPP; | |
5458 | ||
5459 | if (asd->copy_mode) | |
5460 | ret = atomisp_css_copy_configure_output(asd, stream_index, | |
5461 | pix->width, pix->height, | |
5462 | format->planar ? pix->bytesperline : | |
5463 | pix->bytesperline * 8 / format->depth, | |
5464 | format->sh_fmt); | |
5465 | else | |
5466 | ret = configure_output(asd, pix->width, pix->height, | |
5467 | format->planar ? pix->bytesperline : | |
5468 | pix->bytesperline * 8 / format->depth, | |
5469 | format->sh_fmt); | |
5470 | if (ret) { | |
5471 | dev_err(isp->dev, "configure_output %ux%u, format %8.8x\n", | |
5472 | pix->width, pix->height, format->sh_fmt); | |
5473 | return -EINVAL; | |
5474 | } | |
5475 | ||
5476 | if (asd->continuous_mode->val && | |
5477 | (configure_pp_input == atomisp_css_preview_configure_pp_input || | |
5478 | configure_pp_input == atomisp_css_video_configure_pp_input)) { | |
5479 | /* for isp 2.2, configure pp input is available for continuous | |
5480 | * mode */ | |
5481 | ret = configure_pp_input(asd, isp_sink_crop->width, | |
5482 | isp_sink_crop->height); | |
5483 | if (ret) { | |
5484 | dev_err(isp->dev, "configure_pp_input %ux%u\n", | |
5485 | isp_sink_crop->width, | |
5486 | isp_sink_crop->height); | |
5487 | return -EINVAL; | |
5488 | } | |
5489 | } else { | |
5490 | ret = configure_pp_input(asd, isp_sink_crop->width, | |
5491 | isp_sink_crop->height); | |
5492 | if (ret) { | |
5493 | dev_err(isp->dev, "configure_pp_input %ux%u\n", | |
5494 | isp_sink_crop->width, isp_sink_crop->height); | |
5495 | return -EINVAL; | |
5496 | } | |
5497 | } | |
5498 | if (asd->copy_mode) | |
5499 | ret = atomisp_css_copy_get_output_frame_info(asd, stream_index, | |
5500 | output_info); | |
5501 | else | |
5502 | ret = get_frame_info(asd, output_info); | |
5503 | if (ret) { | |
5504 | dev_err(isp->dev, "get_frame_info %ux%u (padded to %u)\n", | |
5505 | pix->width, pix->height, pix->bytesperline); | |
5506 | return -EINVAL; | |
5507 | } | |
5508 | ||
5509 | atomisp_update_grid_info(asd, pipe_id, source_pad); | |
5510 | ||
5511 | /* Free the raw_dump buffer first */ | |
5512 | atomisp_css_frame_free(asd->raw_output_frame); | |
5513 | asd->raw_output_frame = NULL; | |
5514 | ||
5515 | if (!asd->continuous_mode->val && | |
5516 | !asd->params.online_process && !isp->sw_contex.file_input && | |
5517 | atomisp_css_frame_allocate_from_info(&asd->raw_output_frame, | |
5518 | raw_output_info)) | |
5519 | return -ENOMEM; | |
5520 | ||
5521 | return 0; | |
5522 | } | |
5523 | ||
5524 | static void atomisp_get_dis_envelop(struct atomisp_sub_device *asd, | |
5525 | unsigned int width, unsigned int height, | |
5526 | unsigned int *dvs_env_w, unsigned int *dvs_env_h) | |
5527 | { | |
5528 | struct atomisp_device *isp = asd->isp; | |
5529 | ||
5530 | /* if subdev type is SOC camera,we do not need to set DVS */ | |
5531 | if (isp->inputs[asd->input_curr].type == SOC_CAMERA) | |
5532 | asd->params.video_dis_en = 0; | |
5533 | ||
5534 | if (asd->params.video_dis_en && | |
5535 | asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
5536 | /* envelope is 20% of the output resolution */ | |
5537 | /* | |
5538 | * dvs envelope cannot be round up. | |
5539 | * it would cause ISP timeout and color switch issue | |
5540 | */ | |
5541 | *dvs_env_w = rounddown(width / 5, ATOM_ISP_STEP_WIDTH); | |
5542 | *dvs_env_h = rounddown(height / 5, ATOM_ISP_STEP_HEIGHT); | |
5543 | } | |
5544 | ||
5545 | asd->params.dis_proj_data_valid = false; | |
5546 | asd->params.css_update_params_needed = true; | |
5547 | } | |
5548 | ||
5549 | static void atomisp_check_copy_mode(struct atomisp_sub_device *asd, | |
5550 | int source_pad, struct v4l2_format *f) | |
5551 | { | |
5552 | #if defined(ISP2401_NEW_INPUT_SYSTEM) | |
5553 | struct v4l2_mbus_framefmt *sink, *src; | |
5554 | ||
5555 | sink = atomisp_subdev_get_ffmt(&asd->subdev, NULL, | |
5556 | V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK); | |
5557 | src = atomisp_subdev_get_ffmt(&asd->subdev, NULL, | |
5558 | V4L2_SUBDEV_FORMAT_ACTIVE, source_pad); | |
5559 | ||
5560 | if ((sink->code == src->code && | |
5561 | sink->width == f->fmt.pix.width && | |
5562 | sink->height == f->fmt.pix.height) || | |
5563 | ((asd->isp->inputs[asd->input_curr].type == SOC_CAMERA) && | |
5564 | (asd->isp->inputs[asd->input_curr].camera_caps-> | |
5565 | sensor[asd->sensor_curr].stream_num > 1))) | |
5566 | asd->copy_mode = true; | |
5567 | else | |
5568 | #endif | |
5569 | /* Only used for the new input system */ | |
5570 | asd->copy_mode = false; | |
5571 | ||
5572 | dev_dbg(asd->isp->dev, "copy_mode: %d\n", asd->copy_mode); | |
5573 | ||
5574 | } | |
5575 | ||
5576 | static int atomisp_set_fmt_to_snr(struct video_device *vdev, | |
5577 | struct v4l2_format *f, unsigned int pixelformat, | |
5578 | unsigned int padding_w, unsigned int padding_h, | |
5579 | unsigned int dvs_env_w, unsigned int dvs_env_h) | |
5580 | { | |
5581 | struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; | |
5582 | const struct atomisp_format_bridge *format; | |
5583 | struct v4l2_subdev_pad_config pad_cfg; | |
5584 | struct v4l2_subdev_format vformat = { | |
5585 | .which = V4L2_SUBDEV_FORMAT_TRY, | |
5586 | }; | |
5587 | struct v4l2_mbus_framefmt *ffmt = &vformat.format; | |
5588 | struct v4l2_mbus_framefmt *req_ffmt; | |
5589 | struct atomisp_device *isp = asd->isp; | |
5590 | struct atomisp_input_stream_info *stream_info = | |
5591 | (struct atomisp_input_stream_info *)ffmt->reserved; | |
5592 | uint16_t stream_index = ATOMISP_INPUT_STREAM_GENERAL; | |
5593 | int source_pad = atomisp_subdev_source_pad(vdev); | |
5594 | struct v4l2_subdev_fh fh; | |
5595 | int ret; | |
5596 | ||
5597 | v4l2_fh_init(&fh.vfh, vdev); | |
5598 | ||
5599 | stream_index = atomisp_source_pad_to_stream_id(asd, source_pad); | |
5600 | ||
5601 | format = atomisp_get_format_bridge(pixelformat); | |
5602 | if (format == NULL) | |
5603 | return -EINVAL; | |
5604 | ||
5605 | v4l2_fill_mbus_format(ffmt, &f->fmt.pix, format->mbus_code); | |
5606 | ffmt->height += padding_h + dvs_env_h; | |
5607 | ffmt->width += padding_w + dvs_env_w; | |
5608 | ||
5609 | dev_dbg(isp->dev, "s_mbus_fmt: ask %ux%u (padding %ux%u, dvs %ux%u)\n", | |
5610 | ffmt->width, ffmt->height, padding_w, padding_h, | |
5611 | dvs_env_w, dvs_env_h); | |
5612 | ||
5613 | __atomisp_init_stream_info(stream_index, stream_info); | |
5614 | ||
5615 | req_ffmt = ffmt; | |
5616 | ||
5617 | /* Disable dvs if resolution can't be supported by sensor */ | |
5618 | if (asd->params.video_dis_en && | |
5619 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) { | |
2217f784 | 5620 | vformat.which = V4L2_SUBDEV_FORMAT_TRY; |
a49d2536 AC |
5621 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, |
5622 | pad, set_fmt, &pad_cfg, &vformat); | |
5623 | if (ret) | |
5624 | return ret; | |
5625 | if (ffmt->width < req_ffmt->width || | |
4995706d | 5626 | ffmt->height < req_ffmt->height) { |
a49d2536 AC |
5627 | req_ffmt->height -= dvs_env_h; |
5628 | req_ffmt->width -= dvs_env_w; | |
5629 | ffmt = req_ffmt; | |
5630 | dev_warn(isp->dev, | |
5631 | "can not enable video dis due to sensor limitation."); | |
5632 | asd->params.video_dis_en = 0; | |
5633 | } | |
5634 | } | |
5635 | dev_dbg(isp->dev, "sensor width: %d, height: %d\n", | |
5636 | ffmt->width, ffmt->height); | |
2217f784 | 5637 | vformat.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
a49d2536 AC |
5638 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, pad, |
5639 | set_fmt, NULL, &vformat); | |
5640 | if (ret) | |
5641 | return ret; | |
5642 | ||
5643 | __atomisp_update_stream_env(asd, stream_index, stream_info); | |
5644 | ||
5645 | dev_dbg(isp->dev, "sensor width: %d, height: %d\n", | |
5646 | ffmt->width, ffmt->height); | |
5647 | ||
5648 | if (ffmt->width < ATOM_ISP_STEP_WIDTH || | |
5649 | ffmt->height < ATOM_ISP_STEP_HEIGHT) | |
5650 | return -EINVAL; | |
5651 | ||
5652 | if (asd->params.video_dis_en && | |
5653 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO && | |
5654 | (ffmt->width < req_ffmt->width || ffmt->height < req_ffmt->height)) { | |
5655 | dev_warn(isp->dev, | |
5656 | "can not enable video dis due to sensor limitation."); | |
5657 | asd->params.video_dis_en = 0; | |
5658 | } | |
5659 | ||
5660 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, | |
5661 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5662 | ATOMISP_SUBDEV_PAD_SINK, ffmt); | |
5663 | ||
5664 | return css_input_resolution_changed(asd, ffmt); | |
5665 | } | |
5666 | ||
5667 | int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) | |
5668 | { | |
5669 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
5670 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
5671 | struct atomisp_sub_device *asd = pipe->asd; | |
5672 | const struct atomisp_format_bridge *format_bridge; | |
5673 | const struct atomisp_format_bridge *snr_format_bridge; | |
5674 | struct atomisp_css_frame_info output_info, raw_output_info; | |
5675 | struct v4l2_format snr_fmt = *f; | |
5676 | struct v4l2_format backup_fmt = *f, s_fmt = *f; | |
5677 | unsigned int dvs_env_w = 0, dvs_env_h = 0; | |
5678 | unsigned int padding_w = pad_w, padding_h = pad_h; | |
5679 | bool res_overflow = false, crop_needs_override = false; | |
5680 | struct v4l2_mbus_framefmt isp_sink_fmt; | |
5681 | struct v4l2_mbus_framefmt isp_source_fmt = {0}; | |
5682 | struct v4l2_rect isp_sink_crop; | |
5683 | uint16_t source_pad = atomisp_subdev_source_pad(vdev); | |
5684 | struct v4l2_subdev_fh fh; | |
5685 | int ret; | |
5686 | ||
5687 | dev_dbg(isp->dev, | |
5688 | "setting resolution %ux%u on pad %u for asd%d, bytesperline %u\n", | |
5689 | f->fmt.pix.width, f->fmt.pix.height, source_pad, | |
5690 | asd->index, f->fmt.pix.bytesperline); | |
5691 | ||
5692 | if (source_pad >= ATOMISP_SUBDEV_PADS_NUM) | |
5693 | return -EINVAL; | |
5694 | ||
5695 | if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { | |
5696 | dev_warn(isp->dev, "ISP does not support set format while at streaming!\n"); | |
5697 | return -EBUSY; | |
5698 | } | |
5699 | ||
5700 | v4l2_fh_init(&fh.vfh, vdev); | |
5701 | ||
5702 | format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); | |
5703 | if (format_bridge == NULL) | |
5704 | return -EINVAL; | |
5705 | ||
5706 | pipe->sh_fmt = format_bridge->sh_fmt; | |
5707 | pipe->pix.pixelformat = f->fmt.pix.pixelformat; | |
5708 | ||
5709 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VF || | |
5710 | (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW | |
5711 | && asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO)) { | |
5712 | if (asd->fmt_auto->val) { | |
5713 | struct v4l2_rect *capture_comp; | |
5714 | struct v4l2_rect r = {0}; | |
5715 | ||
5716 | r.width = f->fmt.pix.width; | |
5717 | r.height = f->fmt.pix.height; | |
5718 | ||
5719 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) | |
5720 | capture_comp = atomisp_subdev_get_rect( | |
5721 | &asd->subdev, NULL, | |
5722 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5723 | ATOMISP_SUBDEV_PAD_SOURCE_VIDEO, | |
5724 | V4L2_SEL_TGT_COMPOSE); | |
5725 | else | |
5726 | capture_comp = atomisp_subdev_get_rect( | |
5727 | &asd->subdev, NULL, | |
5728 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5729 | ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE, | |
5730 | V4L2_SEL_TGT_COMPOSE); | |
5731 | ||
5732 | if (capture_comp->width < r.width | |
5733 | || capture_comp->height < r.height) { | |
5734 | r.width = capture_comp->width; | |
5735 | r.height = capture_comp->height; | |
5736 | } | |
5737 | ||
5738 | atomisp_subdev_set_selection( | |
5739 | &asd->subdev, fh.pad, | |
5740 | V4L2_SUBDEV_FORMAT_ACTIVE, source_pad, | |
5741 | V4L2_SEL_TGT_COMPOSE, 0, &r); | |
5742 | ||
5743 | f->fmt.pix.width = r.width; | |
5744 | f->fmt.pix.height = r.height; | |
5745 | } | |
5746 | ||
5747 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW && | |
5748 | (asd->isp->inputs[asd->input_curr].type == SOC_CAMERA) && | |
5749 | (asd->isp->inputs[asd->input_curr].camera_caps-> | |
5750 | sensor[asd->sensor_curr].stream_num > 1)) { | |
5751 | /* For M10MO outputing YUV preview images. */ | |
5752 | uint16_t video_index = | |
5753 | atomisp_source_pad_to_stream_id(asd, | |
5754 | ATOMISP_SUBDEV_PAD_SOURCE_VIDEO); | |
5755 | ||
5756 | ret = atomisp_css_copy_get_output_frame_info(asd, | |
5757 | video_index, &output_info); | |
5758 | if (ret) { | |
5759 | dev_err(isp->dev, | |
5760 | "copy_get_output_frame_info ret %i", ret); | |
5761 | return -EINVAL; | |
5762 | } | |
5763 | if (!asd->yuvpp_mode) { | |
5764 | /* | |
5765 | * If viewfinder was configured into copy_mode, | |
5766 | * we switch to using yuvpp pipe instead. | |
5767 | */ | |
5768 | asd->yuvpp_mode = true; | |
5769 | ret = atomisp_css_copy_configure_output( | |
5770 | asd, video_index, 0, 0, 0, 0); | |
5771 | if (ret) { | |
5772 | dev_err(isp->dev, | |
5773 | "failed to disable copy pipe"); | |
5774 | return -EINVAL; | |
5775 | } | |
5776 | ret = atomisp_css_yuvpp_configure_output( | |
5777 | asd, video_index, | |
5778 | output_info.res.width, | |
5779 | output_info.res.height, | |
5780 | output_info.padded_width, | |
5781 | output_info.format); | |
5782 | if (ret) { | |
5783 | dev_err(isp->dev, | |
5784 | "failed to set up yuvpp pipe\n"); | |
5785 | return -EINVAL; | |
5786 | } | |
5787 | atomisp_css_video_enable_online(asd, false); | |
5788 | atomisp_css_preview_enable_online(asd, | |
5789 | ATOMISP_INPUT_STREAM_GENERAL, false); | |
5790 | } | |
5791 | atomisp_css_yuvpp_configure_viewfinder(asd, video_index, | |
5792 | f->fmt.pix.width, f->fmt.pix.height, | |
5793 | format_bridge->planar ? f->fmt.pix.bytesperline | |
5794 | : f->fmt.pix.bytesperline * 8 | |
5795 | / format_bridge->depth, format_bridge->sh_fmt); | |
5796 | atomisp_css_yuvpp_get_viewfinder_frame_info( | |
5797 | asd, video_index, &output_info); | |
5798 | } else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) { | |
5799 | atomisp_css_video_configure_viewfinder(asd, | |
5800 | f->fmt.pix.width, f->fmt.pix.height, | |
5801 | format_bridge->planar ? f->fmt.pix.bytesperline | |
5802 | : f->fmt.pix.bytesperline * 8 | |
5803 | / format_bridge->depth, format_bridge->sh_fmt); | |
5804 | atomisp_css_video_get_viewfinder_frame_info(asd, | |
5805 | &output_info); | |
5806 | asd->copy_mode = false; | |
5807 | } else { | |
5808 | atomisp_css_capture_configure_viewfinder(asd, | |
5809 | f->fmt.pix.width, f->fmt.pix.height, | |
5810 | format_bridge->planar ? f->fmt.pix.bytesperline | |
5811 | : f->fmt.pix.bytesperline * 8 | |
5812 | / format_bridge->depth, format_bridge->sh_fmt); | |
5813 | atomisp_css_capture_get_viewfinder_frame_info(asd, | |
5814 | &output_info); | |
5815 | asd->copy_mode = false; | |
5816 | } | |
5817 | ||
5818 | goto done; | |
5819 | } | |
5820 | /* | |
5821 | * Check whether main resolution configured smaller | |
5822 | * than snapshot resolution. If so, force main resolution | |
5823 | * to be the same as snapshot resolution | |
5824 | */ | |
5825 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE) { | |
5826 | struct v4l2_rect *r; | |
5827 | ||
5828 | r = atomisp_subdev_get_rect( | |
5829 | &asd->subdev, NULL, | |
5830 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5831 | ATOMISP_SUBDEV_PAD_SOURCE_VF, V4L2_SEL_TGT_COMPOSE); | |
5832 | ||
5833 | if (r->width && r->height | |
5834 | && (r->width > f->fmt.pix.width | |
5835 | || r->height > f->fmt.pix.height)) | |
5836 | dev_warn(isp->dev, | |
5837 | "Main Resolution config smaller then Vf Resolution. Force to be equal with Vf Resolution."); | |
5838 | } | |
5839 | ||
5840 | /* Pipeline configuration done through subdevs. Bail out now. */ | |
5841 | if (!asd->fmt_auto->val) | |
5842 | goto set_fmt_to_isp; | |
5843 | ||
5844 | /* get sensor resolution and format */ | |
5845 | ret = atomisp_try_fmt(vdev, &snr_fmt, &res_overflow); | |
5846 | if (ret) | |
5847 | return ret; | |
5848 | f->fmt.pix.width = snr_fmt.fmt.pix.width; | |
5849 | f->fmt.pix.height = snr_fmt.fmt.pix.height; | |
5850 | ||
5851 | snr_format_bridge = | |
5852 | atomisp_get_format_bridge(snr_fmt.fmt.pix.pixelformat); | |
5853 | if (!snr_format_bridge) | |
5854 | return -EINVAL; | |
5855 | ||
5856 | atomisp_subdev_get_ffmt(&asd->subdev, NULL, | |
5857 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5858 | ATOMISP_SUBDEV_PAD_SINK)->code = | |
5859 | snr_format_bridge->mbus_code; | |
5860 | ||
5861 | isp_sink_fmt = *atomisp_subdev_get_ffmt(&asd->subdev, NULL, | |
5862 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5863 | ATOMISP_SUBDEV_PAD_SINK); | |
5864 | ||
5865 | isp_source_fmt.code = format_bridge->mbus_code; | |
5866 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, | |
5867 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5868 | source_pad, &isp_source_fmt); | |
5869 | ||
5870 | if (!atomisp_subdev_format_conversion(asd, source_pad)) { | |
5871 | padding_w = 0; | |
5872 | padding_h = 0; | |
5873 | } else if (IS_BYT) { | |
5874 | padding_w = 12; | |
5875 | padding_h = 12; | |
5876 | } | |
5877 | ||
5878 | /* construct resolution supported by isp */ | |
5879 | if (res_overflow && !asd->continuous_mode->val) { | |
5880 | f->fmt.pix.width = rounddown( | |
5881 | clamp_t(u32, f->fmt.pix.width - padding_w, | |
5882 | ATOM_ISP_MIN_WIDTH, | |
5883 | ATOM_ISP_MAX_WIDTH), ATOM_ISP_STEP_WIDTH); | |
5884 | f->fmt.pix.height = rounddown( | |
5885 | clamp_t(u32, f->fmt.pix.height - padding_h, | |
5886 | ATOM_ISP_MIN_HEIGHT, | |
5887 | ATOM_ISP_MAX_HEIGHT), ATOM_ISP_STEP_HEIGHT); | |
5888 | } | |
5889 | ||
5890 | atomisp_get_dis_envelop(asd, f->fmt.pix.width, f->fmt.pix.height, | |
5891 | &dvs_env_w, &dvs_env_h); | |
5892 | ||
5893 | if (asd->continuous_mode->val) { | |
5894 | struct v4l2_rect *r; | |
5895 | ||
5896 | r = atomisp_subdev_get_rect( | |
5897 | &asd->subdev, NULL, | |
5898 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5899 | ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE, | |
5900 | V4L2_SEL_TGT_COMPOSE); | |
5901 | /* | |
5902 | * The ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE should get resolutions | |
5903 | * properly set otherwise, it should not be the capture_pad. | |
5904 | */ | |
5905 | if (r->width && r->height) | |
5906 | asd->capture_pad = ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE; | |
5907 | else | |
5908 | asd->capture_pad = source_pad; | |
5909 | } else { | |
5910 | asd->capture_pad = source_pad; | |
5911 | } | |
5912 | /* | |
5913 | * set format info to sensor | |
5914 | * In continuous mode, resolution is set only if it is higher than | |
5915 | * existing value. This because preview pipe will be configured after | |
5916 | * capture pipe and usually has lower resolution than capture pipe. | |
5917 | */ | |
5918 | if (!asd->continuous_mode->val || | |
5919 | isp_sink_fmt.width < (f->fmt.pix.width + padding_w + dvs_env_w) || | |
4995706d | 5920 | isp_sink_fmt.height < (f->fmt.pix.height + padding_h + |
a49d2536 AC |
5921 | dvs_env_h)) { |
5922 | /* | |
5923 | * For jpeg or custom raw format the sensor will return constant | |
5924 | * width and height. Because we already had quried try_mbus_fmt, | |
5925 | * f->fmt.pix.width and f->fmt.pix.height has been changed to | |
5926 | * this fixed width and height. So we cannot select the correct | |
5927 | * resolution with that information. So use the original width | |
5928 | * and height while set_mbus_fmt() so actual resolutions are | |
5929 | * being used in while set media bus format. | |
5930 | */ | |
5931 | s_fmt = *f; | |
5932 | if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG || | |
5933 | f->fmt.pix.pixelformat == V4L2_PIX_FMT_CUSTOM_M10MO_RAW) { | |
5934 | s_fmt.fmt.pix.width = backup_fmt.fmt.pix.width; | |
5935 | s_fmt.fmt.pix.height = backup_fmt.fmt.pix.height; | |
5936 | } | |
5937 | ret = atomisp_set_fmt_to_snr(vdev, &s_fmt, | |
5938 | f->fmt.pix.pixelformat, padding_w, | |
5939 | padding_h, dvs_env_w, dvs_env_h); | |
5940 | if (ret) | |
5941 | return -EINVAL; | |
5942 | ||
5943 | atomisp_csi_lane_config(isp); | |
5944 | crop_needs_override = true; | |
5945 | } | |
5946 | ||
5947 | atomisp_check_copy_mode(asd, source_pad, &backup_fmt); | |
5948 | asd->yuvpp_mode = false; /* Reset variable */ | |
5949 | ||
5950 | isp_sink_crop = *atomisp_subdev_get_rect(&asd->subdev, NULL, | |
5951 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5952 | ATOMISP_SUBDEV_PAD_SINK, | |
5953 | V4L2_SEL_TGT_CROP); | |
5954 | ||
5955 | /* Try to enable YUV downscaling if ISP input is 10 % (either | |
5956 | * width or height) bigger than the desired result. */ | |
5957 | if (isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || | |
5958 | isp_sink_crop.height * 9 / 10 < f->fmt.pix.height || | |
5959 | (atomisp_subdev_format_conversion(asd, source_pad) && | |
4995706d | 5960 | ((asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && |
a49d2536 AC |
5961 | !asd->continuous_mode->val) || |
5962 | asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER))) { | |
5963 | /* for continuous mode, preview size might be smaller than | |
5964 | * still capture size. if preview size still needs crop, | |
5965 | * pick the larger one between crop size of preview and | |
5966 | * still capture. | |
5967 | */ | |
5968 | if (asd->continuous_mode->val | |
5969 | && source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW | |
5970 | && !crop_needs_override) { | |
5971 | isp_sink_crop.width = | |
5972 | max_t(unsigned int, f->fmt.pix.width, | |
5973 | isp_sink_crop.width); | |
5974 | isp_sink_crop.height = | |
5975 | max_t(unsigned int, f->fmt.pix.height, | |
5976 | isp_sink_crop.height); | |
5977 | } else { | |
5978 | isp_sink_crop.width = f->fmt.pix.width; | |
5979 | isp_sink_crop.height = f->fmt.pix.height; | |
5980 | } | |
5981 | ||
5982 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
5983 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5984 | ATOMISP_SUBDEV_PAD_SINK, | |
5985 | V4L2_SEL_TGT_CROP, | |
5986 | V4L2_SEL_FLAG_KEEP_CONFIG, | |
5987 | &isp_sink_crop); | |
5988 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
5989 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
5990 | source_pad, V4L2_SEL_TGT_COMPOSE, | |
5991 | 0, &isp_sink_crop); | |
5992 | } else if (IS_MOFD) { | |
5993 | struct v4l2_rect main_compose = {0}; | |
5994 | ||
5995 | main_compose.width = isp_sink_crop.width; | |
5996 | main_compose.height = | |
5997 | DIV_ROUND_UP(main_compose.width * f->fmt.pix.height, | |
5998 | f->fmt.pix.width); | |
5999 | if (main_compose.height > isp_sink_crop.height) { | |
6000 | main_compose.height = isp_sink_crop.height; | |
6001 | main_compose.width = | |
6002 | DIV_ROUND_UP(main_compose.height * | |
6003 | f->fmt.pix.width, | |
6004 | f->fmt.pix.height); | |
6005 | } | |
6006 | ||
6007 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
6008 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
6009 | source_pad, | |
6010 | V4L2_SEL_TGT_COMPOSE, 0, | |
6011 | &main_compose); | |
6012 | } else { | |
6013 | struct v4l2_rect sink_crop = {0}; | |
6014 | struct v4l2_rect main_compose = {0}; | |
6015 | ||
6016 | main_compose.width = f->fmt.pix.width; | |
6017 | main_compose.height = f->fmt.pix.height; | |
6018 | ||
6019 | #ifndef ISP2401 | |
6020 | /* WORKAROUND: this override is universally enabled in | |
6021 | * GMIN to work around a CTS failures (GMINL-539) | |
6022 | * which appears to be related by a hardware | |
6023 | * performance limitation. It's unclear why this | |
6024 | * particular code triggers the issue. */ | |
6025 | if (1 || | |
6026 | crop_needs_override) { | |
6027 | #else | |
6028 | if (crop_needs_override) { | |
6029 | #endif | |
6030 | if (isp_sink_crop.width * main_compose.height > | |
6031 | isp_sink_crop.height * main_compose.width) { | |
6032 | sink_crop.height = isp_sink_crop.height; | |
6033 | sink_crop.width = DIV_NEAREST_STEP( | |
6034 | sink_crop.height * | |
6035 | f->fmt.pix.width, | |
6036 | f->fmt.pix.height, | |
6037 | ATOM_ISP_STEP_WIDTH); | |
6038 | } else { | |
6039 | sink_crop.width = isp_sink_crop.width; | |
6040 | sink_crop.height = DIV_NEAREST_STEP( | |
6041 | sink_crop.width * | |
6042 | f->fmt.pix.height, | |
6043 | f->fmt.pix.width, | |
6044 | ATOM_ISP_STEP_HEIGHT); | |
6045 | } | |
6046 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
6047 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
6048 | ATOMISP_SUBDEV_PAD_SINK, | |
6049 | V4L2_SEL_TGT_CROP, | |
6050 | V4L2_SEL_FLAG_KEEP_CONFIG, | |
6051 | &sink_crop); | |
6052 | } | |
6053 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
6054 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
6055 | source_pad, | |
6056 | V4L2_SEL_TGT_COMPOSE, 0, | |
6057 | &main_compose); | |
6058 | } | |
6059 | ||
6060 | set_fmt_to_isp: | |
6061 | ret = atomisp_set_fmt_to_isp(vdev, &output_info, &raw_output_info, | |
6062 | &f->fmt.pix, source_pad); | |
6063 | if (ret) | |
6064 | return -EINVAL; | |
6065 | done: | |
6066 | pipe->pix.width = f->fmt.pix.width; | |
6067 | pipe->pix.height = f->fmt.pix.height; | |
6068 | pipe->pix.pixelformat = f->fmt.pix.pixelformat; | |
6069 | if (format_bridge->planar) { | |
6070 | pipe->pix.bytesperline = output_info.padded_width; | |
6071 | pipe->pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * | |
6072 | DIV_ROUND_UP(format_bridge->depth * | |
6073 | output_info.padded_width, 8)); | |
6074 | } else { | |
6075 | pipe->pix.bytesperline = | |
6076 | DIV_ROUND_UP(format_bridge->depth * | |
6077 | output_info.padded_width, 8); | |
6078 | pipe->pix.sizeimage = | |
6079 | PAGE_ALIGN(f->fmt.pix.height * pipe->pix.bytesperline); | |
6080 | ||
6081 | } | |
6082 | if (f->fmt.pix.field == V4L2_FIELD_ANY) | |
6083 | f->fmt.pix.field = V4L2_FIELD_NONE; | |
6084 | pipe->pix.field = f->fmt.pix.field; | |
6085 | ||
6086 | f->fmt.pix = pipe->pix; | |
6087 | f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width * | |
6088 | pipe->pix.height * 2); | |
6089 | ||
6090 | pipe->capq.field = f->fmt.pix.field; | |
6091 | ||
6092 | /* | |
6093 | * If in video 480P case, no GFX throttle | |
6094 | */ | |
6095 | if (asd->run_mode->val == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO && | |
6096 | f->fmt.pix.width == 720 && f->fmt.pix.height == 480) | |
6097 | isp->need_gfx_throttle = false; | |
6098 | else | |
6099 | isp->need_gfx_throttle = true; | |
6100 | ||
6101 | return 0; | |
6102 | } | |
6103 | ||
6104 | int atomisp_set_fmt_file(struct video_device *vdev, struct v4l2_format *f) | |
6105 | { | |
6106 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
6107 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
6108 | struct atomisp_sub_device *asd = pipe->asd; | |
6109 | struct v4l2_mbus_framefmt ffmt = {0}; | |
6110 | const struct atomisp_format_bridge *format_bridge; | |
6111 | struct v4l2_subdev_fh fh; | |
6112 | int ret; | |
6113 | ||
6114 | v4l2_fh_init(&fh.vfh, vdev); | |
6115 | ||
6116 | dev_dbg(isp->dev, "setting fmt %ux%u 0x%x for file inject\n", | |
6117 | f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); | |
6118 | ret = atomisp_try_fmt_file(isp, f); | |
6119 | if (ret) { | |
6120 | dev_err(isp->dev, "atomisp_try_fmt_file err: %d\n", ret); | |
6121 | return ret; | |
6122 | } | |
6123 | ||
6124 | format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); | |
6125 | if (format_bridge == NULL) { | |
6126 | dev_dbg(isp->dev, "atomisp_get_format_bridge err! fmt:0x%x\n", | |
6127 | f->fmt.pix.pixelformat); | |
6128 | return -EINVAL; | |
6129 | } | |
6130 | ||
6131 | pipe->pix = f->fmt.pix; | |
6132 | atomisp_css_input_set_mode(asd, CSS_INPUT_MODE_FIFO); | |
6133 | atomisp_css_input_configure_port(asd, | |
6134 | __get_mipi_port(isp, ATOMISP_CAMERA_PORT_PRIMARY), 2, 0xffff4, | |
6135 | 0, 0, 0, 0); | |
6136 | ffmt.width = f->fmt.pix.width; | |
6137 | ffmt.height = f->fmt.pix.height; | |
6138 | ffmt.code = format_bridge->mbus_code; | |
6139 | ||
6140 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, V4L2_SUBDEV_FORMAT_ACTIVE, | |
6141 | ATOMISP_SUBDEV_PAD_SINK, &ffmt); | |
6142 | ||
6143 | return 0; | |
6144 | } | |
6145 | ||
6146 | int atomisp_set_shading_table(struct atomisp_sub_device *asd, | |
6147 | struct atomisp_shading_table *user_shading_table) | |
6148 | { | |
6149 | struct atomisp_css_shading_table *shading_table; | |
6150 | struct atomisp_css_shading_table *free_table; | |
6151 | unsigned int len_table; | |
6152 | int i; | |
6153 | int ret = 0; | |
6154 | ||
6155 | if (!user_shading_table) | |
6156 | return -EINVAL; | |
6157 | ||
6158 | if (!user_shading_table->enable) { | |
6159 | atomisp_css_set_shading_table(asd, NULL); | |
6160 | asd->params.sc_en = 0; | |
6161 | return 0; | |
6162 | } | |
6163 | ||
6164 | /* If enabling, all tables must be set */ | |
6165 | for (i = 0; i < ATOMISP_NUM_SC_COLORS; i++) { | |
6166 | if (!user_shading_table->data[i]) | |
6167 | return -EINVAL; | |
6168 | } | |
6169 | ||
6170 | /* Shading table size per color */ | |
6171 | if (user_shading_table->width > SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR || | |
6172 | user_shading_table->height > SH_CSS_MAX_SCTBL_HEIGHT_PER_COLOR) | |
6173 | return -EINVAL; | |
6174 | ||
6175 | shading_table = atomisp_css_shading_table_alloc( | |
6176 | user_shading_table->width, user_shading_table->height); | |
6177 | if (!shading_table) | |
6178 | return -ENOMEM; | |
6179 | ||
6180 | len_table = user_shading_table->width * user_shading_table->height * | |
6181 | ATOMISP_SC_TYPE_SIZE; | |
6182 | for (i = 0; i < ATOMISP_NUM_SC_COLORS; i++) { | |
6183 | ret = copy_from_user(shading_table->data[i], | |
6184 | user_shading_table->data[i], len_table); | |
6185 | if (ret) { | |
6186 | free_table = shading_table; | |
6187 | ret = -EFAULT; | |
6188 | goto out; | |
6189 | } | |
6190 | } | |
6191 | shading_table->sensor_width = user_shading_table->sensor_width; | |
6192 | shading_table->sensor_height = user_shading_table->sensor_height; | |
6193 | shading_table->fraction_bits = user_shading_table->fraction_bits; | |
6194 | ||
6195 | free_table = asd->params.css_param.shading_table; | |
6196 | asd->params.css_param.shading_table = shading_table; | |
6197 | atomisp_css_set_shading_table(asd, shading_table); | |
6198 | asd->params.sc_en = 1; | |
6199 | ||
6200 | out: | |
6201 | if (free_table != NULL) | |
6202 | atomisp_css_shading_table_free(free_table); | |
6203 | ||
6204 | return ret; | |
6205 | } | |
6206 | ||
6207 | /*Turn off ISP dphy */ | |
6208 | int atomisp_ospm_dphy_down(struct atomisp_device *isp) | |
6209 | { | |
6210 | unsigned long flags; | |
6211 | u32 reg; | |
6212 | ||
6213 | dev_dbg(isp->dev, "%s\n", __func__); | |
6214 | ||
6215 | /* if ISP timeout, we can force powerdown */ | |
6216 | if (isp->isp_timeout) | |
6217 | goto done; | |
6218 | ||
6219 | if (!atomisp_dev_users(isp)) | |
6220 | goto done; | |
6221 | ||
6222 | spin_lock_irqsave(&isp->lock, flags); | |
6223 | isp->sw_contex.power_state = ATOM_ISP_POWER_DOWN; | |
6224 | spin_unlock_irqrestore(&isp->lock, flags); | |
6225 | done: | |
6226 | /* | |
6227 | * MRFLD IUNIT DPHY is located in an always-power-on island | |
6228 | * MRFLD HW design need all CSI ports are disabled before | |
6229 | * powering down the IUNIT. | |
6230 | */ | |
6231 | pci_read_config_dword(isp->pdev, MRFLD_PCI_CSI_CONTROL, ®); | |
6232 | reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK; | |
6233 | pci_write_config_dword(isp->pdev, MRFLD_PCI_CSI_CONTROL, reg); | |
6234 | return 0; | |
6235 | } | |
6236 | ||
6237 | /*Turn on ISP dphy */ | |
6238 | int atomisp_ospm_dphy_up(struct atomisp_device *isp) | |
6239 | { | |
6240 | unsigned long flags; | |
6241 | dev_dbg(isp->dev, "%s\n", __func__); | |
6242 | ||
6243 | spin_lock_irqsave(&isp->lock, flags); | |
6244 | isp->sw_contex.power_state = ATOM_ISP_POWER_UP; | |
6245 | spin_unlock_irqrestore(&isp->lock, flags); | |
6246 | ||
6247 | return 0; | |
6248 | } | |
6249 | ||
6250 | ||
6251 | int atomisp_exif_makernote(struct atomisp_sub_device *asd, | |
6252 | struct atomisp_makernote_info *config) | |
6253 | { | |
6254 | struct v4l2_control ctrl; | |
6255 | struct atomisp_device *isp = asd->isp; | |
6256 | ||
6257 | ctrl.id = V4L2_CID_FOCAL_ABSOLUTE; | |
6258 | if (v4l2_g_ctrl | |
6259 | (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl)) { | |
6260 | dev_warn(isp->dev, "failed to g_ctrl for focal length\n"); | |
6261 | return -EINVAL; | |
6262 | } else { | |
6263 | config->focal_length = ctrl.value; | |
6264 | } | |
6265 | ||
6266 | ctrl.id = V4L2_CID_FNUMBER_ABSOLUTE; | |
6267 | if (v4l2_g_ctrl | |
6268 | (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl)) { | |
6269 | dev_warn(isp->dev, "failed to g_ctrl for f-number\n"); | |
6270 | return -EINVAL; | |
6271 | } else { | |
6272 | config->f_number_curr = ctrl.value; | |
6273 | } | |
6274 | ||
6275 | ctrl.id = V4L2_CID_FNUMBER_RANGE; | |
6276 | if (v4l2_g_ctrl | |
6277 | (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl)) { | |
6278 | dev_warn(isp->dev, "failed to g_ctrl for f number range\n"); | |
6279 | return -EINVAL; | |
6280 | } else { | |
6281 | config->f_number_range = ctrl.value; | |
6282 | } | |
6283 | ||
6284 | return 0; | |
6285 | } | |
6286 | ||
6287 | int atomisp_offline_capture_configure(struct atomisp_sub_device *asd, | |
6288 | struct atomisp_cont_capture_conf *cvf_config) | |
6289 | { | |
6290 | struct v4l2_ctrl *c; | |
6291 | ||
6292 | /* | |
6293 | * In case of M10MO ZSL capture case, we need to issue a separate | |
6294 | * capture request to M10MO which will output captured jpeg image | |
6295 | */ | |
6296 | c = v4l2_ctrl_find( | |
6297 | asd->isp->inputs[asd->input_curr].camera->ctrl_handler, | |
6298 | V4L2_CID_START_ZSL_CAPTURE); | |
6299 | if (c) { | |
6300 | int ret; | |
6301 | dev_dbg(asd->isp->dev, "%s trigger ZSL capture request\n", | |
6302 | __func__); | |
6303 | /* TODO: use the cvf_config */ | |
6304 | ret = v4l2_ctrl_s_ctrl(c, 1); | |
6305 | if (ret) | |
6306 | return ret; | |
6307 | ||
6308 | return v4l2_ctrl_s_ctrl(c, 0); | |
6309 | } | |
6310 | ||
6311 | asd->params.offline_parm = *cvf_config; | |
6312 | ||
6313 | if (asd->params.offline_parm.num_captures) { | |
6314 | if (asd->streaming == ATOMISP_DEVICE_STREAMING_DISABLED) { | |
6315 | unsigned int init_raw_num; | |
6316 | ||
6317 | if (asd->enable_raw_buffer_lock->val) { | |
6318 | init_raw_num = | |
6319 | ATOMISP_CSS2_NUM_OFFLINE_INIT_CONTINUOUS_FRAMES_LOCK_EN; | |
6320 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && | |
6321 | asd->params.video_dis_en) | |
6322 | init_raw_num += | |
6323 | ATOMISP_CSS2_NUM_DVS_FRAME_DELAY; | |
6324 | } else { | |
6325 | init_raw_num = | |
6326 | ATOMISP_CSS2_NUM_OFFLINE_INIT_CONTINUOUS_FRAMES; | |
6327 | } | |
6328 | ||
6329 | /* TODO: this can be removed once user-space | |
6330 | * has been updated to use control API */ | |
6331 | asd->continuous_raw_buffer_size->val = | |
6332 | max_t(int, | |
6333 | asd->continuous_raw_buffer_size->val, | |
6334 | asd->params.offline_parm. | |
6335 | num_captures + init_raw_num); | |
6336 | asd->continuous_raw_buffer_size->val = | |
6337 | min_t(int, ATOMISP_CONT_RAW_FRAMES, | |
6338 | asd->continuous_raw_buffer_size->val); | |
6339 | } | |
6340 | asd->continuous_mode->val = true; | |
6341 | } else { | |
6342 | asd->continuous_mode->val = false; | |
6343 | __enable_continuous_mode(asd, false); | |
6344 | } | |
6345 | ||
6346 | return 0; | |
6347 | } | |
6348 | ||
6349 | /* | |
6350 | * set auto exposure metering window to camera sensor | |
6351 | */ | |
6352 | int atomisp_s_ae_window(struct atomisp_sub_device *asd, | |
6353 | struct atomisp_ae_window *arg) | |
6354 | { | |
6355 | struct atomisp_device *isp = asd->isp; | |
a49d2536 | 6356 | /* Coverity CID 298071 - initialzize struct */ |
a49d2536 AC |
6357 | struct v4l2_subdev_selection sel = { 0 }; |
6358 | ||
6359 | sel.r.left = arg->x_left; | |
6360 | sel.r.top = arg->y_top; | |
6361 | sel.r.width = arg->x_right - arg->x_left + 1; | |
6362 | sel.r.height = arg->y_bottom - arg->y_top + 1; | |
6363 | ||
6364 | if (v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
4995706d | 6365 | pad, set_selection, NULL, &sel)) { |
a49d2536 AC |
6366 | dev_err(isp->dev, "failed to call sensor set_selection.\n"); |
6367 | return -EINVAL; | |
6368 | } | |
6369 | ||
6370 | return 0; | |
6371 | } | |
6372 | ||
6373 | int atomisp_flash_enable(struct atomisp_sub_device *asd, int num_frames) | |
6374 | { | |
6375 | struct atomisp_device *isp = asd->isp; | |
6376 | ||
6377 | if (num_frames < 0) { | |
6378 | dev_dbg(isp->dev, "%s ERROR: num_frames: %d\n", __func__, | |
6379 | num_frames); | |
6380 | return -EINVAL; | |
6381 | } | |
6382 | /* a requested flash is still in progress. */ | |
6383 | if (num_frames && asd->params.flash_state != ATOMISP_FLASH_IDLE) { | |
6384 | dev_dbg(isp->dev, "%s flash busy: %d frames left: %d\n", | |
6385 | __func__, asd->params.flash_state, | |
6386 | asd->params.num_flash_frames); | |
6387 | return -EBUSY; | |
6388 | } | |
6389 | ||
6390 | asd->params.num_flash_frames = num_frames; | |
6391 | asd->params.flash_state = ATOMISP_FLASH_REQUESTED; | |
6392 | return 0; | |
6393 | } | |
6394 | ||
6395 | int atomisp_source_pad_to_stream_id(struct atomisp_sub_device *asd, | |
6396 | uint16_t source_pad) | |
6397 | { | |
6398 | int stream_id; | |
6399 | struct atomisp_device *isp = asd->isp; | |
6400 | ||
6401 | if (isp->inputs[asd->input_curr].camera_caps-> | |
6402 | sensor[asd->sensor_curr].stream_num == 1) | |
6403 | return ATOMISP_INPUT_STREAM_GENERAL; | |
6404 | ||
6405 | switch (source_pad) { | |
6406 | case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: | |
6407 | stream_id = ATOMISP_INPUT_STREAM_CAPTURE; | |
6408 | break; | |
6409 | case ATOMISP_SUBDEV_PAD_SOURCE_VF: | |
6410 | stream_id = ATOMISP_INPUT_STREAM_POSTVIEW; | |
6411 | break; | |
6412 | case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: | |
6413 | stream_id = ATOMISP_INPUT_STREAM_PREVIEW; | |
6414 | break; | |
6415 | case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: | |
6416 | stream_id = ATOMISP_INPUT_STREAM_VIDEO; | |
6417 | break; | |
6418 | default: | |
6419 | stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
6420 | } | |
6421 | ||
6422 | return stream_id; | |
6423 | } | |
6424 | ||
6425 | bool atomisp_is_vf_pipe(struct atomisp_video_pipe *pipe) | |
6426 | { | |
6427 | struct atomisp_sub_device *asd = pipe->asd; | |
6428 | ||
6429 | if (pipe == &asd->video_out_vf) | |
6430 | return true; | |
6431 | ||
6432 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && | |
6433 | pipe == &asd->video_out_preview) | |
6434 | return true; | |
6435 | ||
6436 | return false; | |
6437 | } | |
6438 | ||
6439 | static int __checking_exp_id(struct atomisp_sub_device *asd, int exp_id) | |
6440 | { | |
6441 | struct atomisp_device *isp = asd->isp; | |
6442 | ||
6443 | if (!asd->enable_raw_buffer_lock->val) { | |
6444 | dev_warn(isp->dev, "%s Raw Buffer Lock is disable.\n", __func__); | |
6445 | return -EINVAL; | |
6446 | } | |
6447 | if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) { | |
6448 | dev_err(isp->dev, "%s streaming %d invalid exp_id %d.\n", | |
6449 | __func__, exp_id, asd->streaming); | |
6450 | return -EINVAL; | |
6451 | } | |
6452 | if ((exp_id > ATOMISP_MAX_EXP_ID) || (exp_id <= 0)) { | |
6453 | dev_err(isp->dev, "%s exp_id %d invalid.\n", __func__, exp_id); | |
6454 | return -EINVAL; | |
6455 | } | |
6456 | return 0; | |
6457 | } | |
6458 | ||
6459 | void atomisp_init_raw_buffer_bitmap(struct atomisp_sub_device *asd) | |
6460 | { | |
6461 | unsigned long flags; | |
6462 | spin_lock_irqsave(&asd->raw_buffer_bitmap_lock, flags); | |
6463 | memset(asd->raw_buffer_bitmap, 0, sizeof(asd->raw_buffer_bitmap)); | |
6464 | asd->raw_buffer_locked_count = 0; | |
6465 | spin_unlock_irqrestore(&asd->raw_buffer_bitmap_lock, flags); | |
6466 | } | |
6467 | ||
6468 | int atomisp_set_raw_buffer_bitmap(struct atomisp_sub_device *asd, int exp_id) | |
6469 | { | |
6470 | int *bitmap, bit; | |
6471 | unsigned long flags; | |
6472 | ||
6473 | if (__checking_exp_id(asd, exp_id)) | |
6474 | return -EINVAL; | |
6475 | ||
6476 | bitmap = asd->raw_buffer_bitmap + exp_id / 32; | |
6477 | bit = exp_id % 32; | |
6478 | spin_lock_irqsave(&asd->raw_buffer_bitmap_lock, flags); | |
6479 | (*bitmap) |= (1 << bit); | |
6480 | asd->raw_buffer_locked_count++; | |
6481 | spin_unlock_irqrestore(&asd->raw_buffer_bitmap_lock, flags); | |
6482 | ||
6483 | dev_dbg(asd->isp->dev, "%s: exp_id %d, raw_buffer_locked_count %d\n", | |
6484 | __func__, exp_id, asd->raw_buffer_locked_count); | |
6485 | ||
6486 | /* Check if the raw buffer after next is still locked!!! */ | |
6487 | exp_id += 2; | |
6488 | if (exp_id > ATOMISP_MAX_EXP_ID) | |
6489 | exp_id -= ATOMISP_MAX_EXP_ID; | |
6490 | bitmap = asd->raw_buffer_bitmap + exp_id / 32; | |
6491 | bit = exp_id % 32; | |
6492 | if ((*bitmap) & (1 << bit)) { | |
6493 | int ret; | |
6494 | ||
6495 | /* WORKAROUND unlock the raw buffer compulsively */ | |
6496 | ret = atomisp_css_exp_id_unlock(asd, exp_id); | |
6497 | if (ret) { | |
6498 | dev_err(asd->isp->dev, "%s exp_id is wrapping back to %d but force unlock failed,, err %d.\n", | |
6499 | __func__, exp_id, ret); | |
6500 | return ret; | |
6501 | } | |
6502 | ||
6503 | spin_lock_irqsave(&asd->raw_buffer_bitmap_lock, flags); | |
6504 | (*bitmap) &= ~(1 << bit); | |
6505 | asd->raw_buffer_locked_count--; | |
6506 | spin_unlock_irqrestore(&asd->raw_buffer_bitmap_lock, flags); | |
6507 | dev_warn(asd->isp->dev, "%s exp_id is wrapping back to %d but it is still locked so force unlock it, raw_buffer_locked_count %d\n", | |
6508 | __func__, exp_id, asd->raw_buffer_locked_count); | |
6509 | } | |
6510 | return 0; | |
6511 | } | |
6512 | ||
6513 | static int __is_raw_buffer_locked(struct atomisp_sub_device *asd, int exp_id) | |
6514 | { | |
6515 | int *bitmap, bit; | |
6516 | unsigned long flags; | |
6517 | int ret; | |
6518 | ||
6519 | if (__checking_exp_id(asd, exp_id)) | |
6520 | return -EINVAL; | |
6521 | ||
6522 | bitmap = asd->raw_buffer_bitmap + exp_id / 32; | |
6523 | bit = exp_id % 32; | |
6524 | spin_lock_irqsave(&asd->raw_buffer_bitmap_lock, flags); | |
6525 | ret = ((*bitmap) & (1 << bit)); | |
6526 | spin_unlock_irqrestore(&asd->raw_buffer_bitmap_lock, flags); | |
6527 | return !ret; | |
6528 | } | |
6529 | ||
6530 | static int __clear_raw_buffer_bitmap(struct atomisp_sub_device *asd, int exp_id) | |
6531 | { | |
6532 | int *bitmap, bit; | |
6533 | unsigned long flags; | |
6534 | ||
6535 | if (__is_raw_buffer_locked(asd, exp_id)) | |
6536 | return -EINVAL; | |
6537 | ||
6538 | bitmap = asd->raw_buffer_bitmap + exp_id / 32; | |
6539 | bit = exp_id % 32; | |
6540 | spin_lock_irqsave(&asd->raw_buffer_bitmap_lock, flags); | |
6541 | (*bitmap) &= ~(1 << bit); | |
6542 | asd->raw_buffer_locked_count--; | |
6543 | spin_unlock_irqrestore(&asd->raw_buffer_bitmap_lock, flags); | |
6544 | ||
6545 | dev_dbg(asd->isp->dev, "%s: exp_id %d, raw_buffer_locked_count %d\n", | |
6546 | __func__, exp_id, asd->raw_buffer_locked_count); | |
6547 | return 0; | |
6548 | } | |
6549 | ||
6550 | int atomisp_exp_id_capture(struct atomisp_sub_device *asd, int *exp_id) | |
6551 | { | |
6552 | struct atomisp_device *isp = asd->isp; | |
6553 | int value = *exp_id; | |
6554 | int ret; | |
6555 | ||
6556 | ret = __is_raw_buffer_locked(asd, value); | |
6557 | if (ret) { | |
6558 | dev_err(isp->dev, "%s exp_id %d invalid %d.\n", __func__, value, ret); | |
6559 | return -EINVAL; | |
6560 | } | |
6561 | ||
6562 | dev_dbg(isp->dev, "%s exp_id %d\n", __func__, value); | |
6563 | ret = atomisp_css_exp_id_capture(asd, value); | |
6564 | if (ret) { | |
6565 | dev_err(isp->dev, "%s exp_id %d failed.\n", __func__, value); | |
6566 | return -EIO; | |
6567 | } | |
6568 | return 0; | |
6569 | } | |
6570 | ||
6571 | int atomisp_exp_id_unlock(struct atomisp_sub_device *asd, int *exp_id) | |
6572 | { | |
6573 | struct atomisp_device *isp = asd->isp; | |
6574 | int value = *exp_id; | |
6575 | int ret; | |
6576 | ||
6577 | ret = __clear_raw_buffer_bitmap(asd, value); | |
6578 | if (ret) { | |
6579 | dev_err(isp->dev, "%s exp_id %d invalid %d.\n", __func__, value, ret); | |
6580 | return -EINVAL; | |
6581 | } | |
6582 | ||
6583 | dev_dbg(isp->dev, "%s exp_id %d\n", __func__, value); | |
6584 | ret = atomisp_css_exp_id_unlock(asd, value); | |
6585 | if (ret) | |
6586 | dev_err(isp->dev, "%s exp_id %d failed, err %d.\n", | |
6587 | __func__, value, ret); | |
6588 | ||
6589 | return ret; | |
6590 | } | |
6591 | ||
6592 | int atomisp_enable_dz_capt_pipe(struct atomisp_sub_device *asd, | |
6593 | unsigned int *enable) | |
6594 | { | |
6595 | bool value; | |
6596 | ||
6597 | if (enable == NULL) | |
6598 | return -EINVAL; | |
6599 | ||
6600 | value = *enable > 0 ? true : false; | |
6601 | ||
6602 | atomisp_en_dz_capt_pipe(asd, value); | |
6603 | ||
6604 | return 0; | |
6605 | } | |
6606 | ||
6607 | int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event) | |
6608 | { | |
6609 | if (!event || asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) | |
6610 | return -EINVAL; | |
6611 | ||
6612 | dev_dbg(asd->isp->dev, "%s: trying to inject a fake event 0x%x\n", | |
6613 | __func__, *event); | |
6614 | ||
6615 | switch (*event) { | |
6616 | case V4L2_EVENT_FRAME_SYNC: | |
6617 | atomisp_sof_event(asd); | |
6618 | break; | |
6619 | case V4L2_EVENT_FRAME_END: | |
6620 | atomisp_eof_event(asd, 0); | |
6621 | break; | |
6622 | case V4L2_EVENT_ATOMISP_3A_STATS_READY: | |
6623 | atomisp_3a_stats_ready_event(asd, 0); | |
6624 | break; | |
6625 | case V4L2_EVENT_ATOMISP_METADATA_READY: | |
6626 | atomisp_metadata_ready_event(asd, 0); | |
6627 | break; | |
6628 | default: | |
6629 | return -EINVAL; | |
6630 | } | |
6631 | ||
6632 | return 0; | |
6633 | } | |
6634 | ||
6635 | int atomisp_get_pipe_id(struct atomisp_video_pipe *pipe) | |
6636 | { | |
6637 | struct atomisp_sub_device *asd = pipe->asd; | |
6638 | ||
6639 | if (ATOMISP_USE_YUVPP(asd)) | |
6640 | return CSS_PIPE_ID_YUVPP; | |
6641 | else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) | |
6642 | return CSS_PIPE_ID_VIDEO; | |
6643 | else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) | |
6644 | return CSS_PIPE_ID_CAPTURE; | |
6645 | else if (pipe == &asd->video_out_video_capture) | |
6646 | return CSS_PIPE_ID_VIDEO; | |
6647 | else if (pipe == &asd->video_out_vf) | |
6648 | return CSS_PIPE_ID_CAPTURE; | |
6649 | else if (pipe == &asd->video_out_preview) { | |
6650 | if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) | |
6651 | return CSS_PIPE_ID_VIDEO; | |
6652 | else | |
6653 | return CSS_PIPE_ID_PREVIEW; | |
6654 | } else if (pipe == &asd->video_out_capture) { | |
6655 | if (asd->copy_mode) | |
6656 | return IA_CSS_PIPE_ID_COPY; | |
6657 | else | |
6658 | return CSS_PIPE_ID_CAPTURE; | |
6659 | } | |
6660 | ||
6661 | /* fail through */ | |
6662 | dev_warn(asd->isp->dev, "%s failed to find proper pipe\n", | |
6663 | __func__); | |
6664 | return CSS_PIPE_ID_CAPTURE; | |
6665 | } | |
6666 | ||
6667 | int atomisp_get_invalid_frame_num(struct video_device *vdev, | |
6668 | int *invalid_frame_num) | |
6669 | { | |
6670 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
6671 | struct atomisp_sub_device *asd = pipe->asd; | |
6672 | enum atomisp_css_pipe_id pipe_id; | |
6673 | struct ia_css_pipe_info p_info; | |
6674 | int ret; | |
6675 | ||
6676 | if (asd->isp->inputs[asd->input_curr].camera_caps-> | |
6677 | sensor[asd->sensor_curr].stream_num > 1) { | |
6678 | /* External ISP */ | |
6679 | *invalid_frame_num = 0; | |
6680 | return 0; | |
6681 | } | |
6682 | ||
6683 | pipe_id = atomisp_get_pipe_id(pipe); | |
6684 | if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].pipes[pipe_id]) { | |
6685 | dev_warn(asd->isp->dev, "%s pipe %d has not been created yet, do SET_FMT first!\n", | |
6686 | __func__, pipe_id); | |
6687 | return -EINVAL; | |
6688 | } | |
6689 | ||
6690 | ret = ia_css_pipe_get_info( | |
6691 | asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] | |
6692 | .pipes[pipe_id], &p_info); | |
6693 | if (ret == IA_CSS_SUCCESS) { | |
6694 | *invalid_frame_num = p_info.num_invalid_frames; | |
6695 | return 0; | |
6696 | } else { | |
6697 | dev_warn(asd->isp->dev, "%s get pipe infor failed %d\n", | |
6698 | __func__, ret); | |
6699 | return -EINVAL; | |
6700 | } | |
6701 | } |