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 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/pm_runtime.h> | |
22 | ||
23 | #include <media/v4l2-ioctl.h> | |
24 | #include <media/videobuf-vmalloc.h> | |
25 | ||
26 | #include "atomisp_cmd.h" | |
27 | #include "atomisp_common.h" | |
28 | #include "atomisp_fops.h" | |
29 | #include "atomisp_internal.h" | |
30 | #include "atomisp_ioctl.h" | |
31 | #include "atomisp_compat.h" | |
32 | #include "atomisp_subdev.h" | |
33 | #include "atomisp_v4l2.h" | |
34 | #include "atomisp-regs.h" | |
35 | #include "hmm/hmm.h" | |
36 | ||
37 | #include "hrt/hive_isp_css_mm_hrt.h" | |
38 | ||
39 | #include "type_support.h" | |
40 | #include "device_access/device_access.h" | |
41 | #include "memory_access/memory_access.h" | |
42 | ||
43 | #include "atomisp_acc.h" | |
44 | ||
45 | #define ISP_LEFT_PAD 128 /* equal to 2*NWAY */ | |
46 | ||
47 | /* | |
48 | * input image data, and current frame resolution for test | |
49 | */ | |
50 | #define ISP_PARAM_MMAP_OFFSET 0xfffff000 | |
51 | ||
52 | #define MAGIC_CHECK(is, should) \ | |
53 | do { \ | |
54 | if (unlikely((is) != (should))) { \ | |
55 | pr_err("magic mismatch: %x (expected %x)\n", \ | |
56 | is, should); \ | |
57 | BUG(); \ | |
58 | } \ | |
59 | } while (0) | |
60 | ||
61 | /* | |
62 | * Videobuf ops | |
63 | */ | |
64 | static int atomisp_buf_setup(struct videobuf_queue *vq, unsigned int *count, | |
65 | unsigned int *size) | |
66 | { | |
67 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
68 | ||
69 | *size = pipe->pix.sizeimage; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int atomisp_buf_prepare(struct videobuf_queue *vq, | |
75 | struct videobuf_buffer *vb, | |
76 | enum v4l2_field field) | |
77 | { | |
78 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
79 | ||
80 | vb->size = pipe->pix.sizeimage; | |
81 | vb->width = pipe->pix.width; | |
82 | vb->height = pipe->pix.height; | |
83 | vb->field = field; | |
84 | vb->state = VIDEOBUF_PREPARED; | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | static int atomisp_q_one_metadata_buffer(struct atomisp_sub_device *asd, | |
90 | enum atomisp_input_stream_id stream_id, | |
91 | enum atomisp_css_pipe_id css_pipe_id) | |
92 | { | |
93 | struct atomisp_metadata_buf *metadata_buf; | |
94 | enum atomisp_metadata_type md_type = | |
95 | atomisp_get_metadata_type(asd, css_pipe_id); | |
96 | struct list_head *metadata_list; | |
97 | ||
98 | if (asd->metadata_bufs_in_css[stream_id][css_pipe_id] >= | |
99 | ATOMISP_CSS_Q_DEPTH) | |
100 | return 0; /* we have reached CSS queue depth */ | |
101 | ||
102 | if (!list_empty(&asd->metadata[md_type])) { | |
103 | metadata_list = &asd->metadata[md_type]; | |
104 | } else if (!list_empty(&asd->metadata_ready[md_type])) { | |
105 | metadata_list = &asd->metadata_ready[md_type]; | |
106 | } else { | |
107 | dev_warn(asd->isp->dev, "%s: No metadata buffers available for type %d!\n", | |
108 | __func__, md_type); | |
109 | return -EINVAL; | |
110 | } | |
111 | ||
112 | metadata_buf = list_entry(metadata_list->next, | |
113 | struct atomisp_metadata_buf, list); | |
114 | list_del_init(&metadata_buf->list); | |
115 | ||
116 | if (atomisp_q_metadata_buffer_to_css(asd, metadata_buf, | |
117 | stream_id, css_pipe_id)) { | |
118 | list_add(&metadata_buf->list, metadata_list); | |
119 | return -EINVAL; | |
120 | } else { | |
121 | list_add_tail(&metadata_buf->list, | |
122 | &asd->metadata_in_css[md_type]); | |
123 | } | |
124 | asd->metadata_bufs_in_css[stream_id][css_pipe_id]++; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
c2c611b7 GDS |
129 | static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, |
130 | enum atomisp_input_stream_id stream_id, | |
131 | enum atomisp_css_pipe_id css_pipe_id) | |
a49d2536 AC |
132 | { |
133 | struct atomisp_s3a_buf *s3a_buf; | |
134 | struct list_head *s3a_list; | |
135 | unsigned int exp_id; | |
136 | ||
137 | if (asd->s3a_bufs_in_css[css_pipe_id] >= ATOMISP_CSS_Q_DEPTH) | |
138 | return 0; /* we have reached CSS queue depth */ | |
139 | ||
140 | if (!list_empty(&asd->s3a_stats)) { | |
141 | s3a_list = &asd->s3a_stats; | |
142 | } else if (!list_empty(&asd->s3a_stats_ready)) { | |
143 | s3a_list = &asd->s3a_stats_ready; | |
144 | } else { | |
145 | dev_warn(asd->isp->dev, "%s: No s3a buffers available!\n", | |
146 | __func__); | |
147 | return -EINVAL; | |
148 | } | |
149 | ||
150 | s3a_buf = list_entry(s3a_list->next, struct atomisp_s3a_buf, list); | |
151 | list_del_init(&s3a_buf->list); | |
152 | exp_id = s3a_buf->s3a_data->exp_id; | |
153 | ||
154 | hmm_flush_vmap(s3a_buf->s3a_data->data_ptr); | |
155 | if (atomisp_q_s3a_buffer_to_css(asd, s3a_buf, | |
156 | stream_id, css_pipe_id)) { | |
157 | /* got from head, so return back to the head */ | |
158 | list_add(&s3a_buf->list, s3a_list); | |
159 | return -EINVAL; | |
160 | } else { | |
161 | list_add_tail(&s3a_buf->list, &asd->s3a_stats_in_css); | |
162 | if (s3a_list == &asd->s3a_stats_ready) | |
163 | dev_warn(asd->isp->dev, "%s: drop one s3a stat which has exp_id %d!\n", | |
164 | __func__, exp_id); | |
165 | } | |
166 | ||
167 | asd->s3a_bufs_in_css[css_pipe_id]++; | |
168 | return 0; | |
169 | } | |
170 | ||
c2c611b7 GDS |
171 | static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd, |
172 | enum atomisp_input_stream_id stream_id, | |
173 | enum atomisp_css_pipe_id css_pipe_id) | |
a49d2536 AC |
174 | { |
175 | struct atomisp_dis_buf *dis_buf; | |
176 | unsigned long irqflags; | |
177 | ||
178 | if (asd->dis_bufs_in_css >= ATOMISP_CSS_Q_DEPTH) | |
179 | return 0; /* we have reached CSS queue depth */ | |
180 | ||
181 | spin_lock_irqsave(&asd->dis_stats_lock, irqflags); | |
182 | if (list_empty(&asd->dis_stats)) { | |
183 | spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags); | |
184 | dev_warn(asd->isp->dev, "%s: No dis buffers available!\n", | |
185 | __func__); | |
186 | return -EINVAL; | |
187 | } | |
188 | ||
189 | dis_buf = list_entry(asd->dis_stats.prev, | |
190 | struct atomisp_dis_buf, list); | |
191 | list_del_init(&dis_buf->list); | |
192 | spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags); | |
193 | ||
194 | hmm_flush_vmap(dis_buf->dis_data->data_ptr); | |
195 | if (atomisp_q_dis_buffer_to_css(asd, dis_buf, | |
196 | stream_id, css_pipe_id)) { | |
197 | spin_lock_irqsave(&asd->dis_stats_lock, irqflags); | |
198 | /* got from tail, so return back to the tail */ | |
199 | list_add_tail(&dis_buf->list, &asd->dis_stats); | |
200 | spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags); | |
201 | return -EINVAL; | |
202 | } else { | |
203 | spin_lock_irqsave(&asd->dis_stats_lock, irqflags); | |
204 | list_add_tail(&dis_buf->list, &asd->dis_stats_in_css); | |
205 | spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags); | |
206 | } | |
207 | ||
208 | asd->dis_bufs_in_css++; | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, | |
214 | struct atomisp_video_pipe *pipe, | |
215 | enum atomisp_input_stream_id stream_id, | |
216 | enum atomisp_css_buffer_type css_buf_type, | |
217 | enum atomisp_css_pipe_id css_pipe_id) | |
218 | { | |
219 | struct videobuf_vmalloc_memory *vm_mem; | |
220 | struct atomisp_css_params_with_list *param; | |
221 | struct atomisp_css_dvs_grid_info *dvs_grid = | |
222 | atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info); | |
223 | unsigned long irqflags; | |
224 | int err = 0; | |
225 | ||
226 | while (pipe->buffers_in_css < ATOMISP_CSS_Q_DEPTH) { | |
227 | struct videobuf_buffer *vb; | |
228 | ||
229 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
230 | if (list_empty(&pipe->activeq)) { | |
231 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
232 | return -EINVAL; | |
233 | } | |
234 | vb = list_entry(pipe->activeq.next, | |
235 | struct videobuf_buffer, queue); | |
236 | list_del_init(&vb->queue); | |
237 | vb->state = VIDEOBUF_ACTIVE; | |
238 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
239 | ||
240 | /* | |
241 | * If there is a per_frame setting to apply on the buffer, | |
242 | * do it before buffer en-queueing. | |
243 | */ | |
244 | vm_mem = vb->priv; | |
245 | ||
246 | param = pipe->frame_params[vb->i]; | |
247 | if (param) { | |
248 | atomisp_makeup_css_parameters(asd, | |
249 | &asd->params.css_param.update_flag, | |
250 | ¶m->params); | |
251 | atomisp_apply_css_parameters(asd, ¶m->params); | |
252 | ||
253 | if (param->params.update_flag.dz_config && | |
254 | asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO) { | |
255 | err = atomisp_calculate_real_zoom_region(asd, | |
256 | ¶m->params.dz_config, css_pipe_id); | |
257 | if (!err) | |
258 | atomisp_css_set_dz_config(asd, | |
259 | ¶m->params.dz_config); | |
260 | } | |
261 | atomisp_css_set_isp_config_applied_frame(asd, | |
262 | vm_mem->vaddr); | |
263 | atomisp_css_update_isp_params_on_pipe(asd, | |
264 | asd->stream_env[stream_id].pipes[css_pipe_id]); | |
265 | asd->params.dvs_6axis = (struct atomisp_css_dvs_6axis *) | |
266 | param->params.dvs_6axis; | |
267 | ||
268 | /* | |
269 | * WORKAROUND: | |
270 | * Because the camera halv3 can't ensure to set zoom | |
271 | * region to per_frame setting and global setting at | |
272 | * same time and only set zoom region to pre_frame | |
273 | * setting now.so when the pre_frame setting inculde | |
274 | * zoom region,I will set it to global setting. | |
275 | */ | |
276 | if (param->params.update_flag.dz_config && | |
277 | asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO | |
278 | && !err) { | |
279 | memcpy(&asd->params.css_param.dz_config, | |
280 | ¶m->params.dz_config, | |
281 | sizeof(struct ia_css_dz_config)); | |
282 | asd->params.css_param.update_flag.dz_config = | |
283 | (struct atomisp_dz_config *) | |
284 | &asd->params.css_param.dz_config; | |
285 | asd->params.css_update_params_needed = true; | |
286 | } | |
287 | } | |
288 | /* Enqueue buffer */ | |
289 | err = atomisp_q_video_buffer_to_css(asd, vm_mem, stream_id, | |
290 | css_buf_type, css_pipe_id); | |
291 | if (err) { | |
292 | spin_lock_irqsave(&pipe->irq_lock, irqflags); | |
293 | list_add_tail(&vb->queue, &pipe->activeq); | |
294 | vb->state = VIDEOBUF_QUEUED; | |
295 | spin_unlock_irqrestore(&pipe->irq_lock, irqflags); | |
296 | dev_err(asd->isp->dev, "%s, css q fails: %d\n", | |
297 | __func__, err); | |
298 | return -EINVAL; | |
299 | } | |
300 | pipe->buffers_in_css++; | |
301 | ||
302 | /* enqueue 3A/DIS/metadata buffers */ | |
303 | if (asd->params.curr_grid_info.s3a_grid.enable && | |
304 | css_pipe_id == asd->params.s3a_enabled_pipe && | |
305 | css_buf_type == CSS_BUFFER_TYPE_OUTPUT_FRAME) | |
306 | atomisp_q_one_s3a_buffer(asd, stream_id, | |
307 | css_pipe_id); | |
308 | ||
309 | if (asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. | |
310 | metadata_info.size && | |
311 | css_buf_type == CSS_BUFFER_TYPE_OUTPUT_FRAME) | |
312 | atomisp_q_one_metadata_buffer(asd, stream_id, | |
313 | css_pipe_id); | |
314 | ||
315 | if (dvs_grid && dvs_grid->enable && | |
316 | css_pipe_id == CSS_PIPE_ID_VIDEO && | |
317 | css_buf_type == CSS_BUFFER_TYPE_OUTPUT_FRAME) | |
318 | atomisp_q_one_dis_buffer(asd, stream_id, | |
319 | css_pipe_id); | |
320 | } | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
325 | static int atomisp_get_css_buf_type(struct atomisp_sub_device *asd, | |
326 | enum atomisp_css_pipe_id pipe_id, | |
327 | uint16_t source_pad) | |
328 | { | |
329 | if (ATOMISP_USE_YUVPP(asd)) { | |
330 | /* when run ZSL case */ | |
331 | if (asd->continuous_mode->val && | |
332 | asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { | |
333 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE) | |
334 | return CSS_BUFFER_TYPE_OUTPUT_FRAME; | |
335 | else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) | |
336 | return CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME; | |
337 | else | |
338 | return CSS_BUFFER_TYPE_VF_OUTPUT_FRAME; | |
339 | } | |
340 | ||
341 | /*when run SDV case*/ | |
342 | if (asd->continuous_mode->val && | |
343 | asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
344 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE) | |
345 | return CSS_BUFFER_TYPE_OUTPUT_FRAME; | |
346 | else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) | |
347 | return CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME; | |
348 | else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) | |
349 | return CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME; | |
350 | else | |
351 | return CSS_BUFFER_TYPE_VF_OUTPUT_FRAME; | |
352 | } | |
353 | ||
354 | /*other case: default setting*/ | |
355 | if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE || | |
356 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO || | |
357 | (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW && | |
358 | asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO)) | |
359 | return CSS_BUFFER_TYPE_OUTPUT_FRAME; | |
360 | else | |
361 | return CSS_BUFFER_TYPE_VF_OUTPUT_FRAME; | |
362 | } | |
363 | ||
364 | if (pipe_id == CSS_PIPE_ID_COPY || | |
365 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE || | |
366 | source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO || | |
367 | (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW && | |
368 | asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO)) | |
369 | return CSS_BUFFER_TYPE_OUTPUT_FRAME; | |
370 | else | |
371 | return CSS_BUFFER_TYPE_VF_OUTPUT_FRAME; | |
372 | } | |
373 | ||
374 | static int atomisp_qbuffers_to_css_for_all_pipes(struct atomisp_sub_device *asd) | |
375 | { | |
376 | enum atomisp_css_buffer_type buf_type; | |
377 | enum atomisp_css_pipe_id css_capture_pipe_id = CSS_PIPE_ID_COPY; | |
378 | enum atomisp_css_pipe_id css_preview_pipe_id = CSS_PIPE_ID_COPY; | |
379 | enum atomisp_css_pipe_id css_video_pipe_id = CSS_PIPE_ID_COPY; | |
380 | enum atomisp_input_stream_id input_stream_id; | |
381 | struct atomisp_video_pipe *capture_pipe; | |
382 | struct atomisp_video_pipe *preview_pipe; | |
383 | struct atomisp_video_pipe *video_pipe; | |
384 | ||
385 | capture_pipe = &asd->video_out_capture; | |
386 | preview_pipe = &asd->video_out_preview; | |
387 | video_pipe = &asd->video_out_video_capture; | |
388 | ||
389 | buf_type = atomisp_get_css_buf_type( | |
390 | asd, css_preview_pipe_id, | |
391 | atomisp_subdev_source_pad(&preview_pipe->vdev)); | |
392 | input_stream_id = ATOMISP_INPUT_STREAM_PREVIEW; | |
393 | atomisp_q_video_buffers_to_css(asd, preview_pipe, | |
394 | input_stream_id, | |
395 | buf_type, css_preview_pipe_id); | |
396 | ||
397 | buf_type = atomisp_get_css_buf_type(asd, css_capture_pipe_id, | |
398 | atomisp_subdev_source_pad(&capture_pipe->vdev)); | |
399 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
400 | atomisp_q_video_buffers_to_css(asd, capture_pipe, | |
401 | input_stream_id, | |
402 | buf_type, css_capture_pipe_id); | |
403 | ||
404 | buf_type = atomisp_get_css_buf_type(asd, css_video_pipe_id, | |
405 | atomisp_subdev_source_pad(&video_pipe->vdev)); | |
406 | input_stream_id = ATOMISP_INPUT_STREAM_VIDEO; | |
407 | atomisp_q_video_buffers_to_css(asd, video_pipe, | |
408 | input_stream_id, | |
409 | buf_type, css_video_pipe_id); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | ||
414 | /* queue all available buffers to css */ | |
415 | int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd) | |
416 | { | |
417 | enum atomisp_css_buffer_type buf_type; | |
418 | enum atomisp_css_pipe_id css_capture_pipe_id = CSS_PIPE_ID_NUM; | |
419 | enum atomisp_css_pipe_id css_preview_pipe_id = CSS_PIPE_ID_NUM; | |
420 | enum atomisp_css_pipe_id css_video_pipe_id = CSS_PIPE_ID_NUM; | |
421 | enum atomisp_input_stream_id input_stream_id; | |
422 | struct atomisp_video_pipe *capture_pipe = NULL; | |
423 | struct atomisp_video_pipe *vf_pipe = NULL; | |
424 | struct atomisp_video_pipe *preview_pipe = NULL; | |
425 | struct atomisp_video_pipe *video_pipe = NULL; | |
426 | bool raw_mode = atomisp_is_mbuscode_raw( | |
427 | asd->fmt[asd->capture_pad].fmt.code); | |
428 | ||
429 | if (asd->isp->inputs[asd->input_curr].camera_caps-> | |
430 | sensor[asd->sensor_curr].stream_num == 2 && | |
431 | !asd->yuvpp_mode) | |
432 | return atomisp_qbuffers_to_css_for_all_pipes(asd); | |
433 | ||
434 | if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { | |
435 | video_pipe = &asd->video_out_video_capture; | |
436 | css_video_pipe_id = CSS_PIPE_ID_VIDEO; | |
437 | } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { | |
438 | preview_pipe = &asd->video_out_capture; | |
439 | css_preview_pipe_id = CSS_PIPE_ID_CAPTURE; | |
440 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { | |
441 | if (asd->continuous_mode->val) { | |
442 | capture_pipe = &asd->video_out_capture; | |
443 | vf_pipe = &asd->video_out_vf; | |
444 | css_capture_pipe_id = CSS_PIPE_ID_CAPTURE; | |
445 | } | |
446 | video_pipe = &asd->video_out_video_capture; | |
447 | preview_pipe = &asd->video_out_preview; | |
448 | css_video_pipe_id = CSS_PIPE_ID_VIDEO; | |
449 | css_preview_pipe_id = CSS_PIPE_ID_VIDEO; | |
450 | } else if (asd->continuous_mode->val) { | |
451 | capture_pipe = &asd->video_out_capture; | |
452 | vf_pipe = &asd->video_out_vf; | |
453 | preview_pipe = &asd->video_out_preview; | |
454 | ||
455 | css_preview_pipe_id = CSS_PIPE_ID_PREVIEW; | |
456 | css_capture_pipe_id = CSS_PIPE_ID_CAPTURE; | |
457 | } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { | |
458 | preview_pipe = &asd->video_out_preview; | |
459 | css_preview_pipe_id = CSS_PIPE_ID_PREVIEW; | |
460 | } else { | |
461 | /* ATOMISP_RUN_MODE_STILL_CAPTURE */ | |
462 | capture_pipe = &asd->video_out_capture; | |
463 | if (!raw_mode) | |
464 | vf_pipe = &asd->video_out_vf; | |
465 | css_capture_pipe_id = CSS_PIPE_ID_CAPTURE; | |
466 | } | |
467 | ||
468 | #ifdef ISP2401_NEW_INPUT_SYSTEM | |
469 | if (asd->copy_mode) { | |
470 | css_capture_pipe_id = CSS_PIPE_ID_COPY; | |
471 | css_preview_pipe_id = CSS_PIPE_ID_COPY; | |
472 | css_video_pipe_id = CSS_PIPE_ID_COPY; | |
473 | } | |
474 | #endif | |
475 | ||
476 | if (asd->yuvpp_mode) { | |
477 | capture_pipe = &asd->video_out_capture; | |
478 | video_pipe = &asd->video_out_video_capture; | |
479 | preview_pipe = &asd->video_out_preview; | |
480 | css_capture_pipe_id = CSS_PIPE_ID_COPY; | |
481 | css_video_pipe_id = CSS_PIPE_ID_YUVPP; | |
482 | css_preview_pipe_id = CSS_PIPE_ID_YUVPP; | |
483 | } | |
484 | ||
485 | if (capture_pipe) { | |
486 | buf_type = atomisp_get_css_buf_type( | |
487 | asd, css_capture_pipe_id, | |
488 | atomisp_subdev_source_pad(&capture_pipe->vdev)); | |
489 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
490 | ||
491 | /* | |
492 | * use yuvpp pipe for SOC camera. | |
493 | */ | |
494 | if (ATOMISP_USE_YUVPP(asd)) | |
495 | css_capture_pipe_id = CSS_PIPE_ID_YUVPP; | |
496 | ||
497 | atomisp_q_video_buffers_to_css(asd, capture_pipe, | |
498 | input_stream_id, | |
499 | buf_type, css_capture_pipe_id); | |
500 | } | |
501 | ||
502 | if (vf_pipe) { | |
503 | buf_type = atomisp_get_css_buf_type( | |
504 | asd, css_capture_pipe_id, | |
505 | atomisp_subdev_source_pad(&vf_pipe->vdev)); | |
506 | if (asd->stream_env[ATOMISP_INPUT_STREAM_POSTVIEW].stream) | |
507 | input_stream_id = ATOMISP_INPUT_STREAM_POSTVIEW; | |
508 | else | |
509 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
510 | ||
511 | /* | |
512 | * use yuvpp pipe for SOC camera. | |
513 | */ | |
514 | if (ATOMISP_USE_YUVPP(asd)) | |
515 | css_capture_pipe_id = CSS_PIPE_ID_YUVPP; | |
516 | atomisp_q_video_buffers_to_css(asd, vf_pipe, | |
517 | input_stream_id, | |
518 | buf_type, css_capture_pipe_id); | |
519 | } | |
520 | ||
521 | if (preview_pipe) { | |
522 | buf_type = atomisp_get_css_buf_type( | |
523 | asd, css_preview_pipe_id, | |
524 | atomisp_subdev_source_pad(&preview_pipe->vdev)); | |
525 | if (ATOMISP_SOC_CAMERA(asd) && css_preview_pipe_id == CSS_PIPE_ID_YUVPP) | |
526 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
527 | /* else for ext isp use case */ | |
528 | else if (css_preview_pipe_id == CSS_PIPE_ID_YUVPP) | |
529 | input_stream_id = ATOMISP_INPUT_STREAM_VIDEO; | |
530 | else if (asd->stream_env[ATOMISP_INPUT_STREAM_PREVIEW].stream) | |
531 | input_stream_id = ATOMISP_INPUT_STREAM_PREVIEW; | |
532 | else | |
533 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
534 | ||
535 | /* | |
536 | * use yuvpp pipe for SOC camera. | |
537 | */ | |
538 | if (ATOMISP_USE_YUVPP(asd)) | |
539 | css_preview_pipe_id = CSS_PIPE_ID_YUVPP; | |
540 | ||
541 | atomisp_q_video_buffers_to_css(asd, preview_pipe, | |
542 | input_stream_id, | |
543 | buf_type, css_preview_pipe_id); | |
544 | } | |
545 | ||
546 | if (video_pipe) { | |
547 | buf_type = atomisp_get_css_buf_type( | |
548 | asd, css_video_pipe_id, | |
549 | atomisp_subdev_source_pad(&video_pipe->vdev)); | |
550 | if (asd->stream_env[ATOMISP_INPUT_STREAM_VIDEO].stream) | |
551 | input_stream_id = ATOMISP_INPUT_STREAM_VIDEO; | |
552 | else | |
553 | input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; | |
554 | ||
555 | /* | |
556 | * use yuvpp pipe for SOC camera. | |
557 | */ | |
558 | if (ATOMISP_USE_YUVPP(asd)) | |
559 | css_video_pipe_id = CSS_PIPE_ID_YUVPP; | |
560 | ||
561 | atomisp_q_video_buffers_to_css(asd, video_pipe, | |
562 | input_stream_id, | |
563 | buf_type, css_video_pipe_id); | |
564 | } | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
569 | static void atomisp_buf_queue(struct videobuf_queue *vq, | |
570 | struct videobuf_buffer *vb) | |
571 | { | |
572 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
573 | ||
574 | /* | |
575 | * when a frame buffer meets following conditions, it should be put into | |
576 | * the waiting list: | |
577 | * 1. It is not a main output frame, and it has a per-frame parameter | |
578 | * to go with it. | |
579 | * 2. It is not a main output frame, and the waiting buffer list is not | |
580 | * empty, to keep the FIFO sequence of frame buffer processing, it | |
581 | * is put to waiting list until previous per-frame parameter buffers | |
582 | * get enqueued. | |
583 | */ | |
584 | if (!atomisp_is_vf_pipe(pipe) && | |
585 | (pipe->frame_request_config_id[vb->i] || | |
586 | !list_empty(&pipe->buffers_waiting_for_param))) | |
587 | list_add_tail(&vb->queue, &pipe->buffers_waiting_for_param); | |
588 | else | |
589 | list_add_tail(&vb->queue, &pipe->activeq); | |
590 | ||
591 | vb->state = VIDEOBUF_QUEUED; | |
592 | } | |
593 | ||
594 | static void atomisp_buf_release(struct videobuf_queue *vq, | |
595 | struct videobuf_buffer *vb) | |
596 | { | |
597 | vb->state = VIDEOBUF_NEEDS_INIT; | |
598 | atomisp_videobuf_free_buf(vb); | |
599 | } | |
600 | ||
601 | static int atomisp_buf_setup_output(struct videobuf_queue *vq, | |
602 | unsigned int *count, unsigned int *size) | |
603 | { | |
604 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
605 | ||
606 | *size = pipe->pix.sizeimage; | |
607 | ||
608 | return 0; | |
609 | } | |
610 | ||
611 | static int atomisp_buf_prepare_output(struct videobuf_queue *vq, | |
612 | struct videobuf_buffer *vb, | |
613 | enum v4l2_field field) | |
614 | { | |
615 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
616 | ||
617 | vb->size = pipe->pix.sizeimage; | |
618 | vb->width = pipe->pix.width; | |
619 | vb->height = pipe->pix.height; | |
620 | vb->field = field; | |
621 | vb->state = VIDEOBUF_PREPARED; | |
622 | ||
623 | return 0; | |
624 | } | |
625 | ||
626 | static void atomisp_buf_queue_output(struct videobuf_queue *vq, | |
627 | struct videobuf_buffer *vb) | |
628 | { | |
629 | struct atomisp_video_pipe *pipe = vq->priv_data; | |
630 | ||
631 | list_add_tail(&vb->queue, &pipe->activeq_out); | |
632 | vb->state = VIDEOBUF_QUEUED; | |
633 | } | |
634 | ||
635 | static void atomisp_buf_release_output(struct videobuf_queue *vq, | |
636 | struct videobuf_buffer *vb) | |
637 | { | |
638 | videobuf_vmalloc_free(vb); | |
639 | vb->state = VIDEOBUF_NEEDS_INIT; | |
640 | } | |
641 | ||
38ffab3b | 642 | static const struct videobuf_queue_ops videobuf_qops = { |
a49d2536 AC |
643 | .buf_setup = atomisp_buf_setup, |
644 | .buf_prepare = atomisp_buf_prepare, | |
645 | .buf_queue = atomisp_buf_queue, | |
646 | .buf_release = atomisp_buf_release, | |
647 | }; | |
648 | ||
38ffab3b | 649 | static const struct videobuf_queue_ops videobuf_qops_output = { |
a49d2536 AC |
650 | .buf_setup = atomisp_buf_setup_output, |
651 | .buf_prepare = atomisp_buf_prepare_output, | |
652 | .buf_queue = atomisp_buf_queue_output, | |
653 | .buf_release = atomisp_buf_release_output, | |
654 | }; | |
655 | ||
656 | static int atomisp_init_pipe(struct atomisp_video_pipe *pipe) | |
657 | { | |
658 | /* init locks */ | |
659 | spin_lock_init(&pipe->irq_lock); | |
660 | ||
661 | videobuf_queue_vmalloc_init(&pipe->capq, &videobuf_qops, NULL, | |
662 | &pipe->irq_lock, | |
663 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
664 | V4L2_FIELD_NONE, | |
665 | sizeof(struct atomisp_buffer), pipe, | |
666 | NULL); /* ext_lock: NULL */ | |
667 | ||
668 | videobuf_queue_vmalloc_init(&pipe->outq, &videobuf_qops_output, NULL, | |
669 | &pipe->irq_lock, | |
670 | V4L2_BUF_TYPE_VIDEO_OUTPUT, | |
671 | V4L2_FIELD_NONE, | |
672 | sizeof(struct atomisp_buffer), pipe, | |
673 | NULL); /* ext_lock: NULL */ | |
674 | ||
675 | INIT_LIST_HEAD(&pipe->activeq); | |
676 | INIT_LIST_HEAD(&pipe->activeq_out); | |
677 | INIT_LIST_HEAD(&pipe->buffers_waiting_for_param); | |
678 | INIT_LIST_HEAD(&pipe->per_frame_params); | |
679 | memset(pipe->frame_request_config_id, 0, | |
680 | VIDEO_MAX_FRAME * sizeof(unsigned int)); | |
681 | memset(pipe->frame_params, 0, | |
682 | VIDEO_MAX_FRAME * | |
683 | sizeof(struct atomisp_css_params_with_list *)); | |
684 | ||
685 | return 0; | |
686 | } | |
687 | ||
688 | static void atomisp_dev_init_struct(struct atomisp_device *isp) | |
689 | { | |
690 | unsigned int i; | |
691 | ||
692 | isp->sw_contex.file_input = 0; | |
693 | isp->need_gfx_throttle = true; | |
694 | isp->isp_fatal_error = false; | |
695 | isp->mipi_frame_size = 0; | |
696 | ||
697 | for (i = 0; i < isp->input_cnt; i++) | |
698 | isp->inputs[i].asd = NULL; | |
699 | /* | |
700 | * For Merrifield, frequency is scalable. | |
701 | * After boot-up, the default frequency is 200MHz. | |
702 | */ | |
703 | isp->sw_contex.running_freq = ISP_FREQ_200MHZ; | |
704 | } | |
705 | ||
706 | static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd) | |
707 | { | |
708 | v4l2_ctrl_s_ctrl(asd->run_mode, ATOMISP_RUN_MODE_STILL_CAPTURE); | |
709 | memset(&asd->params.css_param, 0, sizeof(asd->params.css_param)); | |
710 | asd->params.color_effect = V4L2_COLORFX_NONE; | |
711 | asd->params.bad_pixel_en = 1; | |
712 | asd->params.gdc_cac_en = 0; | |
713 | asd->params.video_dis_en = 0; | |
714 | asd->params.sc_en = 0; | |
715 | asd->params.fpn_en = 0; | |
716 | asd->params.xnr_en = 0; | |
717 | asd->params.false_color = 0; | |
718 | asd->params.online_process = 1; | |
719 | asd->params.yuv_ds_en = 0; | |
720 | /* s3a grid not enabled for any pipe */ | |
721 | asd->params.s3a_enabled_pipe = CSS_PIPE_ID_NUM; | |
722 | ||
723 | asd->params.offline_parm.num_captures = 1; | |
724 | asd->params.offline_parm.skip_frames = 0; | |
725 | asd->params.offline_parm.offset = 0; | |
726 | asd->delayed_init = ATOMISP_DELAYED_INIT_NOT_QUEUED; | |
727 | /* Add for channel */ | |
728 | asd->input_curr = 0; | |
729 | ||
730 | asd->mipi_frame_size = 0; | |
731 | asd->copy_mode = false; | |
732 | asd->yuvpp_mode = false; | |
733 | ||
734 | asd->stream_prepared = false; | |
735 | asd->high_speed_mode = false; | |
736 | asd->sensor_array_res.height = 0; | |
737 | asd->sensor_array_res.width = 0; | |
738 | atomisp_css_init_struct(asd); | |
739 | } | |
740 | /* | |
741 | * file operation functions | |
742 | */ | |
c2c611b7 | 743 | static unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd) |
a49d2536 AC |
744 | { |
745 | return asd->video_out_preview.users + | |
746 | asd->video_out_vf.users + | |
747 | asd->video_out_capture.users + | |
748 | asd->video_out_video_capture.users + | |
749 | asd->video_acc.users + | |
750 | asd->video_in.users; | |
751 | } | |
752 | ||
753 | unsigned int atomisp_dev_users(struct atomisp_device *isp) | |
754 | { | |
755 | unsigned int i, sum; | |
756 | for (i = 0, sum = 0; i < isp->num_of_streams; i++) | |
757 | sum += atomisp_subdev_users(&isp->asd[i]); | |
758 | ||
759 | return sum; | |
760 | } | |
761 | ||
762 | static int atomisp_open(struct file *file) | |
763 | { | |
764 | struct video_device *vdev = video_devdata(file); | |
765 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
766 | struct atomisp_video_pipe *pipe = NULL; | |
767 | struct atomisp_acc_pipe *acc_pipe = NULL; | |
768 | struct atomisp_sub_device *asd; | |
769 | bool acc_node = false; | |
770 | int ret; | |
771 | ||
772 | dev_dbg(isp->dev, "open device %s\n", vdev->name); | |
773 | ||
774 | rt_mutex_lock(&isp->mutex); | |
775 | ||
776 | acc_node = !strncmp(vdev->name, "ATOMISP ISP ACC", | |
777 | sizeof(vdev->name)); | |
778 | if (acc_node) { | |
779 | acc_pipe = atomisp_to_acc_pipe(vdev); | |
780 | asd = acc_pipe->asd; | |
781 | } else { | |
782 | pipe = atomisp_to_video_pipe(vdev); | |
783 | asd = pipe->asd; | |
784 | } | |
785 | asd->subdev.devnode = vdev; | |
786 | /* Deferred firmware loading case. */ | |
787 | if (isp->css_env.isp_css_fw.bytes == 0) { | |
788 | isp->firmware = atomisp_load_firmware(isp); | |
789 | if (!isp->firmware) { | |
790 | dev_err(isp->dev, "Failed to load ISP firmware.\n"); | |
791 | ret = -ENOENT; | |
792 | goto error; | |
793 | } | |
794 | ret = atomisp_css_load_firmware(isp); | |
795 | if (ret) { | |
796 | dev_err(isp->dev, "Failed to init css.\n"); | |
797 | goto error; | |
798 | } | |
799 | /* No need to keep FW in memory anymore. */ | |
800 | release_firmware(isp->firmware); | |
801 | isp->firmware = NULL; | |
802 | isp->css_env.isp_css_fw.data = NULL; | |
803 | } | |
804 | ||
805 | if (acc_node && acc_pipe->users) { | |
806 | dev_dbg(isp->dev, "acc node already opened\n"); | |
807 | rt_mutex_unlock(&isp->mutex); | |
808 | return -EBUSY; | |
809 | } else if (acc_node) { | |
810 | goto dev_init; | |
811 | } | |
812 | ||
813 | if (!isp->input_cnt) { | |
814 | dev_err(isp->dev, "no camera attached\n"); | |
815 | ret = -EINVAL; | |
816 | goto error; | |
817 | } | |
818 | ||
819 | /* | |
820 | * atomisp does not allow multiple open | |
821 | */ | |
822 | if (pipe->users) { | |
823 | dev_dbg(isp->dev, "video node already opened\n"); | |
824 | rt_mutex_unlock(&isp->mutex); | |
825 | return -EBUSY; | |
826 | } | |
827 | ||
828 | ret = atomisp_init_pipe(pipe); | |
829 | if (ret) | |
830 | goto error; | |
831 | ||
832 | dev_init: | |
833 | if (atomisp_dev_users(isp)) { | |
834 | dev_dbg(isp->dev, "skip init isp in open\n"); | |
835 | goto init_subdev; | |
836 | } | |
837 | ||
838 | /* runtime power management, turn on ISP */ | |
839 | ret = pm_runtime_get_sync(vdev->v4l2_dev->dev); | |
840 | if (ret < 0) { | |
841 | dev_err(isp->dev, "Failed to power on device\n"); | |
842 | goto error; | |
843 | } | |
844 | ||
845 | if (dypool_enable) { | |
846 | ret = hmm_pool_register(dypool_pgnr, HMM_POOL_TYPE_DYNAMIC); | |
847 | if (ret) | |
848 | dev_err(isp->dev, "Failed to register dynamic memory pool.\n"); | |
849 | } | |
850 | ||
851 | /* Init ISP */ | |
852 | if (atomisp_css_init(isp)) { | |
853 | ret = -EINVAL; | |
854 | /* Need to clean up CSS init if it fails. */ | |
855 | goto css_error; | |
856 | } | |
857 | ||
858 | atomisp_dev_init_struct(isp); | |
859 | ||
860 | ret = v4l2_subdev_call(isp->flash, core, s_power, 1); | |
861 | if (ret < 0 && ret != -ENODEV && ret != -ENOIOCTLCMD) { | |
862 | dev_err(isp->dev, "Failed to power-on flash\n"); | |
863 | goto css_error; | |
864 | } | |
865 | ||
866 | init_subdev: | |
867 | if (atomisp_subdev_users(asd)) | |
868 | goto done; | |
869 | ||
870 | atomisp_subdev_init_struct(asd); | |
871 | ||
872 | done: | |
873 | ||
874 | if (acc_node) | |
875 | acc_pipe->users++; | |
876 | else | |
877 | pipe->users++; | |
878 | rt_mutex_unlock(&isp->mutex); | |
879 | return 0; | |
880 | ||
881 | css_error: | |
882 | atomisp_css_uninit(isp); | |
883 | error: | |
884 | hmm_pool_unregister(HMM_POOL_TYPE_DYNAMIC); | |
885 | pm_runtime_put(vdev->v4l2_dev->dev); | |
886 | rt_mutex_unlock(&isp->mutex); | |
887 | return ret; | |
888 | } | |
889 | ||
890 | static int atomisp_release(struct file *file) | |
891 | { | |
892 | struct video_device *vdev = video_devdata(file); | |
893 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
894 | struct atomisp_video_pipe *pipe; | |
895 | struct atomisp_acc_pipe *acc_pipe; | |
896 | struct atomisp_sub_device *asd; | |
897 | bool acc_node; | |
898 | struct v4l2_requestbuffers req; | |
899 | struct v4l2_subdev_fh fh; | |
900 | struct v4l2_rect clear_compose = {0}; | |
901 | int ret = 0; | |
902 | ||
903 | v4l2_fh_init(&fh.vfh, vdev); | |
904 | ||
905 | req.count = 0; | |
906 | if (isp == NULL) | |
907 | return -EBADF; | |
908 | ||
909 | mutex_lock(&isp->streamoff_mutex); | |
910 | rt_mutex_lock(&isp->mutex); | |
911 | ||
912 | dev_dbg(isp->dev, "release device %s\n", vdev->name); | |
913 | acc_node = !strncmp(vdev->name, "ATOMISP ISP ACC", | |
914 | sizeof(vdev->name)); | |
915 | if (acc_node) { | |
916 | acc_pipe = atomisp_to_acc_pipe(vdev); | |
917 | asd = acc_pipe->asd; | |
918 | } else { | |
919 | pipe = atomisp_to_video_pipe(vdev); | |
920 | asd = pipe->asd; | |
921 | } | |
922 | asd->subdev.devnode = vdev; | |
923 | if (acc_node) { | |
924 | acc_pipe->users--; | |
925 | goto subdev_uninit; | |
926 | } | |
927 | pipe->users--; | |
928 | ||
929 | if (pipe->capq.streaming) | |
930 | dev_warn(isp->dev, | |
931 | "%s: ISP still streaming while closing!", | |
932 | __func__); | |
933 | ||
934 | if (pipe->capq.streaming && | |
935 | __atomisp_streamoff(file, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE)) { | |
936 | dev_err(isp->dev, | |
937 | "atomisp_streamoff failed on release, driver bug"); | |
938 | goto done; | |
939 | } | |
940 | ||
941 | if (pipe->users) | |
942 | goto done; | |
943 | ||
944 | if (__atomisp_reqbufs(file, NULL, &req)) { | |
945 | dev_err(isp->dev, | |
946 | "atomisp_reqbufs failed on release, driver bug"); | |
947 | goto done; | |
948 | } | |
949 | ||
950 | if (pipe->outq.bufs[0]) { | |
951 | mutex_lock(&pipe->outq.vb_lock); | |
952 | videobuf_queue_cancel(&pipe->outq); | |
953 | mutex_unlock(&pipe->outq.vb_lock); | |
954 | } | |
955 | ||
956 | /* | |
957 | * A little trick here: | |
958 | * file injection input resolution is recorded in the sink pad, | |
959 | * therefore can not be cleared when releaseing one device node. | |
960 | * The sink pad setting can only be cleared when all device nodes | |
961 | * get released. | |
962 | */ | |
963 | if (!isp->sw_contex.file_input && asd->fmt_auto->val) { | |
964 | struct v4l2_mbus_framefmt isp_sink_fmt = { 0 }; | |
965 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, | |
966 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
967 | ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt); | |
968 | } | |
969 | subdev_uninit: | |
970 | if (atomisp_subdev_users(asd)) | |
971 | goto done; | |
972 | ||
973 | /* clear the sink pad for file input */ | |
974 | if (isp->sw_contex.file_input && asd->fmt_auto->val) { | |
975 | struct v4l2_mbus_framefmt isp_sink_fmt = { 0 }; | |
976 | atomisp_subdev_set_ffmt(&asd->subdev, fh.pad, | |
977 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
978 | ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt); | |
979 | } | |
980 | ||
981 | atomisp_css_free_stat_buffers(asd); | |
982 | atomisp_free_internal_buffers(asd); | |
983 | ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, | |
984 | core, s_power, 0); | |
985 | if (ret) | |
986 | dev_warn(isp->dev, "Failed to power-off sensor\n"); | |
987 | ||
988 | /* clear the asd field to show this camera is not used */ | |
989 | isp->inputs[asd->input_curr].asd = NULL; | |
990 | asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; | |
991 | ||
992 | if (atomisp_dev_users(isp)) | |
993 | goto done; | |
994 | ||
995 | atomisp_acc_release(asd); | |
996 | ||
997 | atomisp_destroy_pipes_stream_force(asd); | |
998 | atomisp_css_uninit(isp); | |
999 | ||
1000 | if (defer_fw_load) { | |
1001 | atomisp_css_unload_firmware(isp); | |
1002 | isp->css_env.isp_css_fw.data = NULL; | |
1003 | isp->css_env.isp_css_fw.bytes = 0; | |
1004 | } | |
1005 | ||
1006 | hmm_pool_unregister(HMM_POOL_TYPE_DYNAMIC); | |
1007 | ||
1008 | ret = v4l2_subdev_call(isp->flash, core, s_power, 0); | |
1009 | if (ret < 0 && ret != -ENODEV && ret != -ENOIOCTLCMD) | |
1010 | dev_warn(isp->dev, "Failed to power-off flash\n"); | |
1011 | ||
1012 | if (pm_runtime_put_sync(vdev->v4l2_dev->dev) < 0) | |
1013 | dev_err(isp->dev, "Failed to power off device\n"); | |
1014 | ||
1015 | done: | |
1016 | if (!acc_node) { | |
1017 | atomisp_subdev_set_selection(&asd->subdev, fh.pad, | |
1018 | V4L2_SUBDEV_FORMAT_ACTIVE, | |
1019 | atomisp_subdev_source_pad(vdev), | |
1020 | V4L2_SEL_TGT_COMPOSE, 0, | |
1021 | &clear_compose); | |
1022 | } | |
1023 | rt_mutex_unlock(&isp->mutex); | |
1024 | mutex_unlock(&isp->streamoff_mutex); | |
1025 | ||
1026 | return 0; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * Memory help functions for image frame and private parameters | |
1031 | */ | |
1032 | static int do_isp_mm_remap(struct atomisp_device *isp, | |
1033 | struct vm_area_struct *vma, | |
1034 | ia_css_ptr isp_virt, u32 host_virt, u32 pgnr) | |
1035 | { | |
1036 | u32 pfn; | |
1037 | ||
1038 | while (pgnr) { | |
1039 | pfn = hmm_virt_to_phys(isp_virt) >> PAGE_SHIFT; | |
1040 | if (remap_pfn_range(vma, host_virt, pfn, | |
1041 | PAGE_SIZE, PAGE_SHARED)) { | |
1042 | dev_err(isp->dev, "remap_pfn_range err.\n"); | |
1043 | return -EAGAIN; | |
1044 | } | |
1045 | ||
1046 | isp_virt += PAGE_SIZE; | |
1047 | host_virt += PAGE_SIZE; | |
1048 | pgnr--; | |
1049 | } | |
1050 | ||
1051 | return 0; | |
1052 | } | |
1053 | ||
1054 | static int frame_mmap(struct atomisp_device *isp, | |
1055 | const struct atomisp_css_frame *frame, struct vm_area_struct *vma) | |
1056 | { | |
1057 | ia_css_ptr isp_virt; | |
1058 | u32 host_virt; | |
1059 | u32 pgnr; | |
1060 | ||
1061 | if (!frame) { | |
1062 | dev_err(isp->dev, "%s: NULL frame pointer.\n", __func__); | |
1063 | return -EINVAL; | |
1064 | } | |
1065 | ||
1066 | host_virt = vma->vm_start; | |
1067 | isp_virt = frame->data; | |
1068 | atomisp_get_frame_pgnr(isp, frame, &pgnr); | |
1069 | ||
1070 | if (do_isp_mm_remap(isp, vma, isp_virt, host_virt, pgnr)) | |
1071 | return -EAGAIN; | |
1072 | ||
1073 | return 0; | |
1074 | } | |
1075 | ||
1076 | int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q, | |
1077 | struct vm_area_struct *vma) | |
1078 | { | |
1079 | u32 offset = vma->vm_pgoff << PAGE_SHIFT; | |
1080 | int ret = -EINVAL, i; | |
1081 | struct atomisp_device *isp = | |
1082 | ((struct atomisp_video_pipe *)(q->priv_data))->isp; | |
1083 | struct videobuf_vmalloc_memory *vm_mem; | |
1084 | struct videobuf_mapping *map; | |
1085 | ||
1086 | MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); | |
1087 | if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { | |
1088 | dev_err(isp->dev, "map appl bug: PROT_WRITE and MAP_SHARED are required\n"); | |
1089 | return -EINVAL; | |
1090 | } | |
1091 | ||
1092 | mutex_lock(&q->vb_lock); | |
1093 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | |
1094 | struct videobuf_buffer *buf = q->bufs[i]; | |
1095 | if (buf == NULL) | |
1096 | continue; | |
1097 | ||
1098 | map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); | |
95738786 | 1099 | if (!map) { |
a49d2536 AC |
1100 | mutex_unlock(&q->vb_lock); |
1101 | return -ENOMEM; | |
1102 | } | |
1103 | ||
1104 | buf->map = map; | |
1105 | map->q = q; | |
1106 | ||
1107 | buf->baddr = vma->vm_start; | |
1108 | ||
1109 | if (buf && buf->memory == V4L2_MEMORY_MMAP && | |
1110 | buf->boff == offset) { | |
1111 | vm_mem = buf->priv; | |
1112 | ret = frame_mmap(isp, vm_mem->vaddr, vma); | |
1113 | vma->vm_flags |= VM_IO|VM_DONTEXPAND|VM_DONTDUMP; | |
1114 | break; | |
1115 | } | |
1116 | } | |
1117 | mutex_unlock(&q->vb_lock); | |
1118 | ||
1119 | return ret; | |
1120 | } | |
1121 | ||
1122 | /* The input frame contains left and right padding that need to be removed. | |
1123 | * There is always ISP_LEFT_PAD padding on the left side. | |
1124 | * There is also padding on the right (padded_width - width). | |
1125 | */ | |
1126 | static int remove_pad_from_frame(struct atomisp_device *isp, | |
1127 | struct atomisp_css_frame *in_frame, __u32 width, __u32 height) | |
1128 | { | |
1129 | unsigned int i; | |
1130 | unsigned short *buffer; | |
1131 | int ret = 0; | |
1132 | ia_css_ptr load = in_frame->data; | |
1133 | ia_css_ptr store = load; | |
1134 | ||
1135 | buffer = kmalloc(width*sizeof(load), GFP_KERNEL); | |
309167b9 | 1136 | if (!buffer) |
a49d2536 | 1137 | return -ENOMEM; |
a49d2536 AC |
1138 | |
1139 | load += ISP_LEFT_PAD; | |
1140 | for (i = 0; i < height; i++) { | |
08674e98 | 1141 | ret = hmm_load(load, buffer, width*sizeof(load)); |
a49d2536 AC |
1142 | if (ret < 0) |
1143 | goto remove_pad_error; | |
1144 | ||
08674e98 | 1145 | ret = hmm_store(store, buffer, width*sizeof(store)); |
a49d2536 AC |
1146 | if (ret < 0) |
1147 | goto remove_pad_error; | |
1148 | ||
1149 | load += in_frame->info.padded_width; | |
1150 | store += width; | |
1151 | } | |
1152 | ||
1153 | remove_pad_error: | |
1154 | kfree(buffer); | |
1155 | return ret; | |
1156 | } | |
1157 | ||
1158 | static int atomisp_mmap(struct file *file, struct vm_area_struct *vma) | |
1159 | { | |
1160 | struct video_device *vdev = video_devdata(file); | |
1161 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
1162 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
1163 | struct atomisp_sub_device *asd = pipe->asd; | |
1164 | struct atomisp_css_frame *raw_virt_addr; | |
1165 | u32 start = vma->vm_start; | |
1166 | u32 end = vma->vm_end; | |
1167 | u32 size = end - start; | |
1168 | u32 origin_size, new_size; | |
1169 | int ret; | |
1170 | ||
1171 | if (!(vma->vm_flags & (VM_WRITE | VM_READ))) | |
1172 | return -EACCES; | |
1173 | ||
1174 | rt_mutex_lock(&isp->mutex); | |
1175 | ||
1176 | if (!(vma->vm_flags & VM_SHARED)) { | |
1177 | /* Map private buffer. | |
1178 | * Set VM_SHARED to the flags since we need | |
1179 | * to map the buffer page by page. | |
1180 | * Without VM_SHARED, remap_pfn_range() treats | |
1181 | * this kind of mapping as invalid. | |
1182 | */ | |
1183 | vma->vm_flags |= VM_SHARED; | |
1184 | ret = hmm_mmap(vma, vma->vm_pgoff << PAGE_SHIFT); | |
1185 | rt_mutex_unlock(&isp->mutex); | |
1186 | return ret; | |
1187 | } | |
1188 | ||
1189 | /* mmap for ISP offline raw data */ | |
1190 | if (atomisp_subdev_source_pad(vdev) | |
1191 | == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && | |
1192 | vma->vm_pgoff == (ISP_PARAM_MMAP_OFFSET >> PAGE_SHIFT)) { | |
1193 | new_size = pipe->pix.width * pipe->pix.height * 2; | |
1194 | if (asd->params.online_process != 0) { | |
1195 | ret = -EINVAL; | |
1196 | goto error; | |
1197 | } | |
1198 | raw_virt_addr = asd->raw_output_frame; | |
1199 | if (raw_virt_addr == NULL) { | |
1200 | dev_err(isp->dev, "Failed to request RAW frame\n"); | |
1201 | ret = -EINVAL; | |
1202 | goto error; | |
1203 | } | |
1204 | ||
1205 | ret = remove_pad_from_frame(isp, raw_virt_addr, | |
1206 | pipe->pix.width, pipe->pix.height); | |
1207 | if (ret < 0) { | |
1208 | dev_err(isp->dev, "remove pad failed.\n"); | |
1209 | goto error; | |
1210 | } | |
1211 | origin_size = raw_virt_addr->data_bytes; | |
1212 | raw_virt_addr->data_bytes = new_size; | |
1213 | ||
1214 | if (size != PAGE_ALIGN(new_size)) { | |
1215 | dev_err(isp->dev, "incorrect size for mmap ISP Raw Frame\n"); | |
1216 | ret = -EINVAL; | |
1217 | goto error; | |
1218 | } | |
1219 | ||
1220 | if (frame_mmap(isp, raw_virt_addr, vma)) { | |
1221 | dev_err(isp->dev, "frame_mmap failed.\n"); | |
1222 | raw_virt_addr->data_bytes = origin_size; | |
1223 | ret = -EAGAIN; | |
1224 | goto error; | |
1225 | } | |
1226 | raw_virt_addr->data_bytes = origin_size; | |
1227 | vma->vm_flags |= VM_IO|VM_DONTEXPAND|VM_DONTDUMP; | |
1228 | rt_mutex_unlock(&isp->mutex); | |
1229 | return 0; | |
1230 | } | |
1231 | ||
1232 | /* | |
1233 | * mmap for normal frames | |
1234 | */ | |
1235 | if (size != pipe->pix.sizeimage) { | |
1236 | dev_err(isp->dev, "incorrect size for mmap ISP frames\n"); | |
1237 | ret = -EINVAL; | |
1238 | goto error; | |
1239 | } | |
1240 | rt_mutex_unlock(&isp->mutex); | |
1241 | ||
1242 | return atomisp_videobuf_mmap_mapper(&pipe->capq, vma); | |
1243 | ||
1244 | error: | |
1245 | rt_mutex_unlock(&isp->mutex); | |
1246 | ||
1247 | return ret; | |
1248 | } | |
1249 | ||
1250 | static int atomisp_file_mmap(struct file *file, struct vm_area_struct *vma) | |
1251 | { | |
1252 | struct video_device *vdev = video_devdata(file); | |
1253 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
1254 | ||
1255 | return videobuf_mmap_mapper(&pipe->outq, vma); | |
1256 | } | |
1257 | ||
c23e0cb8 | 1258 | static __poll_t atomisp_poll(struct file *file, |
a49d2536 AC |
1259 | struct poll_table_struct *pt) |
1260 | { | |
1261 | struct video_device *vdev = video_devdata(file); | |
1262 | struct atomisp_device *isp = video_get_drvdata(vdev); | |
1263 | struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); | |
1264 | ||
1265 | rt_mutex_lock(&isp->mutex); | |
1266 | if (pipe->capq.streaming != 1) { | |
1267 | rt_mutex_unlock(&isp->mutex); | |
a9a08845 | 1268 | return EPOLLERR; |
a49d2536 AC |
1269 | } |
1270 | rt_mutex_unlock(&isp->mutex); | |
1271 | ||
1272 | return videobuf_poll_stream(file, &pipe->capq, pt); | |
1273 | } | |
1274 | ||
1275 | const struct v4l2_file_operations atomisp_fops = { | |
1276 | .owner = THIS_MODULE, | |
1277 | .open = atomisp_open, | |
1278 | .release = atomisp_release, | |
1279 | .mmap = atomisp_mmap, | |
1280 | .unlocked_ioctl = video_ioctl2, | |
1281 | #ifdef CONFIG_COMPAT | |
1282 | .compat_ioctl32 = atomisp_compat_ioctl32, | |
1283 | #endif | |
1284 | .poll = atomisp_poll, | |
1285 | }; | |
1286 | ||
1287 | const struct v4l2_file_operations atomisp_file_fops = { | |
1288 | .owner = THIS_MODULE, | |
1289 | .open = atomisp_open, | |
1290 | .release = atomisp_release, | |
1291 | .mmap = atomisp_file_mmap, | |
1292 | .unlocked_ioctl = video_ioctl2, | |
1293 | #ifdef CONFIG_COMPAT | |
1294 | .compat_ioctl32 = atomisp_compat_ioctl32, | |
1295 | #endif | |
1296 | .poll = atomisp_poll, | |
1297 | }; | |
1298 |