1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for Medifield PNW Camera Imaging ISP subsystem.
4  *
5  * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
6  *
7  * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
8  */
9 
10 #include <linux/module.h>
11 #include <linux/pm_runtime.h>
12 
13 #include <media/v4l2-ioctl.h>
14 #include <media/videobuf2-vmalloc.h>
15 
16 #include "atomisp_cmd.h"
17 #include "atomisp_common.h"
18 #include "atomisp_fops.h"
19 #include "atomisp_internal.h"
20 #include "atomisp_ioctl.h"
21 #include "atomisp_compat.h"
22 #include "atomisp_subdev.h"
23 #include "atomisp_v4l2.h"
24 #include "atomisp-regs.h"
25 #include "hmm/hmm.h"
26 
27 #include "ia_css_frame.h"
28 #include "type_support.h"
29 #include "device_access/device_access.h"
30 
31 /*
32  * Videobuf2 ops
33  */
atomisp_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])34 static int atomisp_queue_setup(struct vb2_queue *vq,
35 			       unsigned int *nbuffers, unsigned int *nplanes,
36 			       unsigned int sizes[], struct device *alloc_devs[])
37 {
38 	struct atomisp_video_pipe *pipe = container_of(vq, struct atomisp_video_pipe, vb_queue);
39 	int ret;
40 
41 	mutex_lock(&pipe->asd->isp->mutex); /* for get_css_frame_info() / set_fmt() */
42 
43 	/*
44 	 * When VIDIOC_S_FMT has not been called before VIDIOC_REQBUFS, then
45 	 * this will fail. Call atomisp_set_fmt() ourselves and try again.
46 	 */
47 	ret = atomisp_get_css_frame_info(pipe->asd, &pipe->frame_info);
48 	if (ret) {
49 		struct v4l2_format f = {
50 			.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420,
51 			.fmt.pix.width = 10000,
52 			.fmt.pix.height = 10000,
53 		};
54 
55 		ret = atomisp_set_fmt(&pipe->vdev, &f);
56 		if (ret)
57 			goto out;
58 
59 		ret = atomisp_get_css_frame_info(pipe->asd, &pipe->frame_info);
60 		if (ret)
61 			goto out;
62 	}
63 
64 	atomisp_alloc_css_stat_bufs(pipe->asd, ATOMISP_INPUT_STREAM_GENERAL);
65 
66 	*nplanes = 1;
67 	sizes[0] = PAGE_ALIGN(pipe->pix.sizeimage);
68 
69 out:
70 	mutex_unlock(&pipe->asd->isp->mutex);
71 	return ret;
72 }
73 
atomisp_buf_init(struct vb2_buffer * vb)74 static int atomisp_buf_init(struct vb2_buffer *vb)
75 {
76 	struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
77 	struct ia_css_frame *frame = vb_to_frame(vb);
78 	int ret;
79 
80 	ret = ia_css_frame_init_from_info(frame, &pipe->frame_info);
81 	if (ret)
82 		return ret;
83 
84 	if (frame->data_bytes > vb2_plane_size(vb, 0)) {
85 		dev_err(pipe->asd->isp->dev, "Internal error frame.data_bytes(%u) > vb.length(%lu)\n",
86 			frame->data_bytes, vb2_plane_size(vb, 0));
87 		return -EIO;
88 	}
89 
90 	frame->data = hmm_create_from_vmalloc_buf(vb2_plane_size(vb, 0),
91 						  vb2_plane_vaddr(vb, 0));
92 	if (frame->data == mmgr_NULL)
93 		return -ENOMEM;
94 
95 	return 0;
96 }
97 
atomisp_q_one_metadata_buffer(struct atomisp_sub_device * asd,enum atomisp_input_stream_id stream_id,enum ia_css_pipe_id css_pipe_id)98 static int atomisp_q_one_metadata_buffer(struct atomisp_sub_device *asd,
99 	enum atomisp_input_stream_id stream_id,
100 	enum ia_css_pipe_id css_pipe_id)
101 {
102 	struct atomisp_metadata_buf *metadata_buf;
103 	enum atomisp_metadata_type md_type = ATOMISP_MAIN_METADATA;
104 	struct list_head *metadata_list;
105 
106 	if (asd->metadata_bufs_in_css[stream_id][css_pipe_id] >=
107 	    ATOMISP_CSS_Q_DEPTH)
108 		return 0; /* we have reached CSS queue depth */
109 
110 	if (!list_empty(&asd->metadata[md_type])) {
111 		metadata_list = &asd->metadata[md_type];
112 	} else if (!list_empty(&asd->metadata_ready[md_type])) {
113 		metadata_list = &asd->metadata_ready[md_type];
114 	} else {
115 		dev_warn(asd->isp->dev, "%s: No metadata buffers available for type %d!\n",
116 			 __func__, md_type);
117 		return -EINVAL;
118 	}
119 
120 	metadata_buf = list_entry(metadata_list->next,
121 				  struct atomisp_metadata_buf, list);
122 	list_del_init(&metadata_buf->list);
123 
124 	if (atomisp_q_metadata_buffer_to_css(asd, metadata_buf,
125 					     stream_id, css_pipe_id)) {
126 		list_add(&metadata_buf->list, metadata_list);
127 		return -EINVAL;
128 	} else {
129 		list_add_tail(&metadata_buf->list,
130 			      &asd->metadata_in_css[md_type]);
131 	}
132 	asd->metadata_bufs_in_css[stream_id][css_pipe_id]++;
133 
134 	return 0;
135 }
136 
atomisp_q_one_s3a_buffer(struct atomisp_sub_device * asd,enum atomisp_input_stream_id stream_id,enum ia_css_pipe_id css_pipe_id)137 static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd,
138 				    enum atomisp_input_stream_id stream_id,
139 				    enum ia_css_pipe_id css_pipe_id)
140 {
141 	struct atomisp_s3a_buf *s3a_buf;
142 	struct list_head *s3a_list;
143 	unsigned int exp_id;
144 
145 	if (asd->s3a_bufs_in_css[css_pipe_id] >= ATOMISP_CSS_Q_DEPTH)
146 		return 0; /* we have reached CSS queue depth */
147 
148 	if (!list_empty(&asd->s3a_stats)) {
149 		s3a_list = &asd->s3a_stats;
150 	} else if (!list_empty(&asd->s3a_stats_ready)) {
151 		s3a_list = &asd->s3a_stats_ready;
152 	} else {
153 		dev_warn(asd->isp->dev, "%s: No s3a buffers available!\n",
154 			 __func__);
155 		return -EINVAL;
156 	}
157 
158 	s3a_buf = list_entry(s3a_list->next, struct atomisp_s3a_buf, list);
159 	list_del_init(&s3a_buf->list);
160 	exp_id = s3a_buf->s3a_data->exp_id;
161 
162 	hmm_flush_vmap(s3a_buf->s3a_data->data_ptr);
163 	if (atomisp_q_s3a_buffer_to_css(asd, s3a_buf,
164 					stream_id, css_pipe_id)) {
165 		/* got from head, so return back to the head */
166 		list_add(&s3a_buf->list, s3a_list);
167 		return -EINVAL;
168 	} else {
169 		list_add_tail(&s3a_buf->list, &asd->s3a_stats_in_css);
170 		if (s3a_list == &asd->s3a_stats_ready)
171 			dev_dbg(asd->isp->dev, "drop one s3a stat with exp_id %d\n", exp_id);
172 	}
173 
174 	asd->s3a_bufs_in_css[css_pipe_id]++;
175 	return 0;
176 }
177 
atomisp_q_one_dis_buffer(struct atomisp_sub_device * asd,enum atomisp_input_stream_id stream_id,enum ia_css_pipe_id css_pipe_id)178 static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd,
179 				    enum atomisp_input_stream_id stream_id,
180 				    enum ia_css_pipe_id css_pipe_id)
181 {
182 	struct atomisp_dis_buf *dis_buf;
183 	unsigned long irqflags;
184 
185 	if (asd->dis_bufs_in_css >=  ATOMISP_CSS_Q_DEPTH)
186 		return 0; /* we have reached CSS queue depth */
187 
188 	spin_lock_irqsave(&asd->dis_stats_lock, irqflags);
189 	if (list_empty(&asd->dis_stats)) {
190 		spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);
191 		dev_warn(asd->isp->dev, "%s: No dis buffers available!\n",
192 			 __func__);
193 		return -EINVAL;
194 	}
195 
196 	dis_buf = list_entry(asd->dis_stats.prev,
197 			     struct atomisp_dis_buf, list);
198 	list_del_init(&dis_buf->list);
199 	spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);
200 
201 	hmm_flush_vmap(dis_buf->dis_data->data_ptr);
202 	if (atomisp_q_dis_buffer_to_css(asd, dis_buf,
203 					stream_id, css_pipe_id)) {
204 		spin_lock_irqsave(&asd->dis_stats_lock, irqflags);
205 		/* got from tail, so return back to the tail */
206 		list_add_tail(&dis_buf->list, &asd->dis_stats);
207 		spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);
208 		return -EINVAL;
209 	} else {
210 		spin_lock_irqsave(&asd->dis_stats_lock, irqflags);
211 		list_add_tail(&dis_buf->list, &asd->dis_stats_in_css);
212 		spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);
213 	}
214 
215 	asd->dis_bufs_in_css++;
216 
217 	return 0;
218 }
219 
atomisp_q_video_buffers_to_css(struct atomisp_sub_device * asd,struct atomisp_video_pipe * pipe,enum atomisp_input_stream_id stream_id,enum ia_css_buffer_type css_buf_type,enum ia_css_pipe_id css_pipe_id)220 static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
221 					  struct atomisp_video_pipe *pipe,
222 					  enum atomisp_input_stream_id stream_id,
223 					  enum ia_css_buffer_type css_buf_type,
224 					  enum ia_css_pipe_id css_pipe_id)
225 {
226 	struct atomisp_css_params_with_list *param;
227 	struct ia_css_dvs_grid_info *dvs_grid =
228 	    atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);
229 	unsigned long irqflags;
230 	int space, err = 0;
231 
232 	lockdep_assert_held(&asd->isp->mutex);
233 
234 	if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM))
235 		return -EINVAL;
236 
237 	if (pipe->stopping)
238 		return -EINVAL;
239 
240 	space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe);
241 	while (space--) {
242 		struct ia_css_frame *frame;
243 
244 		spin_lock_irqsave(&pipe->irq_lock, irqflags);
245 		frame = list_first_entry_or_null(&pipe->activeq, struct ia_css_frame, queue);
246 		if (frame)
247 			list_move_tail(&frame->queue, &pipe->buffers_in_css);
248 		spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
249 
250 		if (!frame)
251 			return -EINVAL;
252 
253 		/*
254 		 * If there is a per_frame setting to apply on the buffer,
255 		 * do it before buffer en-queueing.
256 		 */
257 		param = pipe->frame_params[frame->vb.vb2_buf.index];
258 		if (param) {
259 			atomisp_makeup_css_parameters(asd,
260 						      &asd->params.css_param.update_flag,
261 						      &param->params);
262 			atomisp_apply_css_parameters(asd, &param->params);
263 
264 			if (param->params.update_flag.dz_config &&
265 			    asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO) {
266 				err = atomisp_calculate_real_zoom_region(asd,
267 					&param->params.dz_config, css_pipe_id);
268 				if (!err)
269 					asd->params.config.dz_config = &param->params.dz_config;
270 			}
271 			atomisp_css_set_isp_config_applied_frame(asd, frame);
272 			atomisp_css_update_isp_params_on_pipe(asd,
273 							      asd->stream_env[stream_id].pipes[css_pipe_id]);
274 			asd->params.dvs_6axis = (struct ia_css_dvs_6axis_config *)
275 						param->params.dvs_6axis;
276 
277 			/*
278 			 * WORKAROUND:
279 			 * Because the camera halv3 can't ensure to set zoom
280 			 * region to per_frame setting and global setting at
281 			 * same time and only set zoom region to pre_frame
282 			 * setting now.so when the pre_frame setting include
283 			 * zoom region,I will set it to global setting.
284 			 */
285 			if (param->params.update_flag.dz_config &&
286 			    asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO
287 			    && !err) {
288 				memcpy(&asd->params.css_param.dz_config,
289 				       &param->params.dz_config,
290 				       sizeof(struct ia_css_dz_config));
291 				asd->params.css_param.update_flag.dz_config =
292 				    (struct atomisp_dz_config *)
293 				    &asd->params.css_param.dz_config;
294 				asd->params.css_update_params_needed = true;
295 			}
296 			pipe->frame_params[frame->vb.vb2_buf.index] = NULL;
297 		}
298 		/* Enqueue buffer */
299 		err = atomisp_q_video_buffer_to_css(asd, frame, stream_id,
300 						    css_buf_type, css_pipe_id);
301 		if (err) {
302 			spin_lock_irqsave(&pipe->irq_lock, irqflags);
303 			list_move_tail(&frame->queue, &pipe->activeq);
304 			spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
305 			dev_err(asd->isp->dev, "%s, css q fails: %d\n",
306 				__func__, err);
307 			return -EINVAL;
308 		}
309 
310 		/* enqueue 3A/DIS/metadata buffers */
311 		if (asd->params.curr_grid_info.s3a_grid.enable &&
312 		    css_pipe_id == asd->params.s3a_enabled_pipe &&
313 		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
314 			atomisp_q_one_s3a_buffer(asd, stream_id,
315 						 css_pipe_id);
316 
317 		if (asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info.
318 		    metadata_info.size &&
319 		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
320 			atomisp_q_one_metadata_buffer(asd, stream_id,
321 						      css_pipe_id);
322 
323 		if (dvs_grid && dvs_grid->enable &&
324 		    css_pipe_id == IA_CSS_PIPE_ID_VIDEO &&
325 		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
326 			atomisp_q_one_dis_buffer(asd, stream_id,
327 						 css_pipe_id);
328 	}
329 
330 	return 0;
331 }
332 
333 /* queue all available buffers to css */
atomisp_qbuffers_to_css(struct atomisp_sub_device * asd)334 int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd)
335 {
336 	enum ia_css_pipe_id pipe_id;
337 
338 	if (asd->copy_mode) {
339 		pipe_id = IA_CSS_PIPE_ID_COPY;
340 	} else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) {
341 		pipe_id = IA_CSS_PIPE_ID_VIDEO;
342 	} else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) {
343 		pipe_id = IA_CSS_PIPE_ID_CAPTURE;
344 	} else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
345 		pipe_id = IA_CSS_PIPE_ID_VIDEO;
346 	} else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) {
347 		pipe_id = IA_CSS_PIPE_ID_PREVIEW;
348 	} else {
349 		/* ATOMISP_RUN_MODE_STILL_CAPTURE */
350 		pipe_id = IA_CSS_PIPE_ID_CAPTURE;
351 	}
352 
353 	atomisp_q_video_buffers_to_css(asd, &asd->video_out,
354 				       ATOMISP_INPUT_STREAM_GENERAL,
355 				       IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, pipe_id);
356 	return 0;
357 }
358 
atomisp_buf_queue(struct vb2_buffer * vb)359 static void atomisp_buf_queue(struct vb2_buffer *vb)
360 {
361 	struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
362 	struct ia_css_frame *frame = vb_to_frame(vb);
363 	struct atomisp_sub_device *asd = pipe->asd;
364 	unsigned long irqflags;
365 	int ret;
366 
367 	mutex_lock(&asd->isp->mutex);
368 
369 	ret = atomisp_pipe_check(pipe, false);
370 	if (ret || pipe->stopping) {
371 		spin_lock_irqsave(&pipe->irq_lock, irqflags);
372 		atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
373 		spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
374 		goto out_unlock;
375 	}
376 
377 	/* FIXME this ugliness comes from the original atomisp buffer handling */
378 	if (!(vb->skip_cache_sync_on_finish && vb->skip_cache_sync_on_prepare))
379 		wbinvd();
380 
381 	pipe->frame_params[vb->index] = NULL;
382 
383 	spin_lock_irqsave(&pipe->irq_lock, irqflags);
384 	/*
385 	 * when a frame buffer meets following conditions, it should be put into
386 	 * the waiting list:
387 	 * 1.  It is not a main output frame, and it has a per-frame parameter
388 	 *     to go with it.
389 	 * 2.  It is not a main output frame, and the waiting buffer list is not
390 	 *     empty, to keep the FIFO sequence of frame buffer processing, it
391 	 *     is put to waiting list until previous per-frame parameter buffers
392 	 *     get enqueued.
393 	 */
394 	if (pipe->frame_request_config_id[vb->index] ||
395 	    !list_empty(&pipe->buffers_waiting_for_param))
396 		list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param);
397 	else
398 		list_add_tail(&frame->queue, &pipe->activeq);
399 
400 	spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
401 
402 	/* TODO: do this better, not best way to queue to css */
403 	if (asd->streaming) {
404 		if (!list_empty(&pipe->buffers_waiting_for_param))
405 			atomisp_handle_parameter_and_buffer(pipe);
406 		else
407 			atomisp_qbuffers_to_css(asd);
408 	}
409 
410 out_unlock:
411 	mutex_unlock(&asd->isp->mutex);
412 }
413 
atomisp_buf_cleanup(struct vb2_buffer * vb)414 static void atomisp_buf_cleanup(struct vb2_buffer *vb)
415 {
416 	struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
417 	struct ia_css_frame *frame = vb_to_frame(vb);
418 	int index = frame->vb.vb2_buf.index;
419 
420 	pipe->frame_request_config_id[index] = 0;
421 	pipe->frame_params[index] = NULL;
422 
423 	hmm_free(frame->data);
424 }
425 
426 const struct vb2_ops atomisp_vb2_ops = {
427 	.queue_setup		= atomisp_queue_setup,
428 	.buf_init		= atomisp_buf_init,
429 	.buf_cleanup		= atomisp_buf_cleanup,
430 	.buf_queue		= atomisp_buf_queue,
431 	.start_streaming	= atomisp_start_streaming,
432 	.stop_streaming		= atomisp_stop_streaming,
433 };
434 
atomisp_dev_init_struct(struct atomisp_device * isp)435 static void atomisp_dev_init_struct(struct atomisp_device *isp)
436 {
437 	isp->isp_fatal_error = false;
438 
439 	/*
440 	 * For Merrifield, frequency is scalable.
441 	 * After boot-up, the default frequency is 200MHz.
442 	 */
443 	isp->running_freq = ISP_FREQ_200MHZ;
444 }
445 
atomisp_subdev_init_struct(struct atomisp_sub_device * asd)446 static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd)
447 {
448 	memset(&asd->params.css_param, 0, sizeof(asd->params.css_param));
449 	asd->params.color_effect = V4L2_COLORFX_NONE;
450 	asd->params.bad_pixel_en = true;
451 	asd->params.gdc_cac_en = false;
452 	asd->params.video_dis_en = false;
453 	asd->params.sc_en = false;
454 	asd->params.fpn_en = false;
455 	asd->params.xnr_en = false;
456 	asd->params.false_color = 0;
457 	asd->params.yuv_ds_en = 0;
458 	/* s3a grid not enabled for any pipe */
459 	asd->params.s3a_enabled_pipe = IA_CSS_PIPE_ID_NUM;
460 
461 	asd->copy_mode = false;
462 
463 	asd->stream_prepared = false;
464 	asd->high_speed_mode = false;
465 	asd->sensor_array_res.height = 0;
466 	asd->sensor_array_res.width = 0;
467 	atomisp_css_init_struct(asd);
468 }
469 
470 /*
471  * file operation functions
472  */
atomisp_open(struct file * file)473 static int atomisp_open(struct file *file)
474 {
475 	struct video_device *vdev = video_devdata(file);
476 	struct atomisp_device *isp = video_get_drvdata(vdev);
477 	struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
478 	struct atomisp_sub_device *asd = pipe->asd;
479 	int ret;
480 
481 	dev_dbg(isp->dev, "open device %s\n", vdev->name);
482 
483 	ret = v4l2_fh_open(file);
484 	if (ret)
485 		return ret;
486 
487 	mutex_lock(&isp->mutex);
488 
489 	if (!isp->input_cnt) {
490 		dev_err(isp->dev, "no camera attached\n");
491 		ret = -EINVAL;
492 		goto error;
493 	}
494 
495 	/*
496 	 * atomisp does not allow multiple open
497 	 */
498 	if (pipe->users) {
499 		dev_dbg(isp->dev, "video node already opened\n");
500 		ret = -EBUSY;
501 		goto error;
502 	}
503 
504 	/* runtime power management, turn on ISP */
505 	ret = pm_runtime_resume_and_get(vdev->v4l2_dev->dev);
506 	if (ret < 0) {
507 		dev_err(isp->dev, "Failed to power on device\n");
508 		goto error;
509 	}
510 
511 	atomisp_dev_init_struct(isp);
512 	atomisp_subdev_init_struct(asd);
513 
514 	pipe->users++;
515 	mutex_unlock(&isp->mutex);
516 	return 0;
517 
518 error:
519 	mutex_unlock(&isp->mutex);
520 	v4l2_fh_release(file);
521 	return ret;
522 }
523 
atomisp_release(struct file * file)524 static int atomisp_release(struct file *file)
525 {
526 	struct video_device *vdev = video_devdata(file);
527 	struct atomisp_device *isp = video_get_drvdata(vdev);
528 	struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
529 	struct atomisp_sub_device *asd = pipe->asd;
530 	struct v4l2_subdev_fh fh;
531 
532 	v4l2_fh_init(&fh.vfh, vdev);
533 
534 	dev_dbg(isp->dev, "release device %s\n", vdev->name);
535 
536 	/* Note file must not be used after this! */
537 	vb2_fop_release(file);
538 
539 	mutex_lock(&isp->mutex);
540 
541 	pipe->users--;
542 
543 	atomisp_css_free_stat_buffers(asd);
544 	atomisp_free_internal_buffers(asd);
545 
546 	atomisp_s_sensor_power(isp, asd->input_curr, 0);
547 
548 	atomisp_destroy_pipes_stream(asd);
549 
550 	if (pm_runtime_put_sync(vdev->v4l2_dev->dev) < 0)
551 		dev_err(isp->dev, "Failed to power off device\n");
552 
553 	mutex_unlock(&isp->mutex);
554 	return 0;
555 }
556 
557 const struct v4l2_file_operations atomisp_fops = {
558 	.owner = THIS_MODULE,
559 	.open = atomisp_open,
560 	.release = atomisp_release,
561 	.mmap = vb2_fop_mmap,
562 	.poll = vb2_fop_poll,
563 	.unlocked_ioctl = video_ioctl2,
564 #ifdef CONFIG_COMPAT
565 	/*
566 	 * this was removed because of bugs, the interface
567 	 * needs to be made safe for compat tasks instead.
568 	.compat_ioctl32 = atomisp_compat_ioctl32,
569 	 */
570 #endif
571 };
572