File Index
Files and directories
Sign in
FILE

atomisp_fops.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/atomisp/pci/atomisp_fops.c C
  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 */
 34static 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
 69out:
 70	mutex_unlock(&pipe->asd->isp->mutex);
 71	return ret;
 72}
 73
 74static 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
 98static 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
137static 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
178static 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
220static 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	space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe);
238	while (space--) {
239		struct ia_css_frame *frame;
240
241		spin_lock_irqsave(&pipe->irq_lock, irqflags);
242		frame = list_first_entry_or_null(&pipe->activeq, struct ia_css_frame, queue);
243		if (frame)
244			list_move_tail(&frame->queue, &pipe->buffers_in_css);
245		spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
246
247		if (!frame)
248			return -EINVAL;
249
250		/*
251		 * If there is a per_frame setting to apply on the buffer,
252		 * do it before buffer en-queueing.
253		 */
254		param = pipe->frame_params[frame->vb.vb2_buf.index];
255		if (param) {
256			atomisp_makeup_css_parameters(asd,
257						      &asd->params.css_param.update_flag,
258						      &param->params);
259			atomisp_apply_css_parameters(asd, &param->params);
260
261			if (param->params.update_flag.dz_config &&
262			    asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO) {
263				err = atomisp_calculate_real_zoom_region(asd,
264					&param->params.dz_config, css_pipe_id);
265				if (!err)
266					asd->params.config.dz_config = &param->params.dz_config;
267			}
268			atomisp_css_set_isp_config_applied_frame(asd, frame);
269			atomisp_css_update_isp_params_on_pipe(asd,
270							      asd->stream_env[stream_id].pipes[css_pipe_id]);
271			asd->params.dvs_6axis = (struct ia_css_dvs_6axis_config *)
272						param->params.dvs_6axis;
273
274			/*
275			 * WORKAROUND:
276			 * Because the camera halv3 can't ensure to set zoom
277			 * region to per_frame setting and global setting at
278			 * same time and only set zoom region to pre_frame
279			 * setting now.so when the pre_frame setting include
280			 * zoom region,I will set it to global setting.
281			 */
282			if (param->params.update_flag.dz_config &&
283			    asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO
284			    && !err) {
285				memcpy(&asd->params.css_param.dz_config,
286				       &param->params.dz_config,
287				       sizeof(struct ia_css_dz_config));
288				asd->params.css_param.update_flag.dz_config =
289				    (struct atomisp_dz_config *)
290				    &asd->params.css_param.dz_config;
291				asd->params.css_update_params_needed = true;
292			}
293			pipe->frame_params[frame->vb.vb2_buf.index] = NULL;
294		}
295		/* Enqueue buffer */
296		err = atomisp_q_video_buffer_to_css(asd, frame, stream_id,
297						    css_buf_type, css_pipe_id);
298		if (err) {
299			spin_lock_irqsave(&pipe->irq_lock, irqflags);
300			list_move_tail(&frame->queue, &pipe->activeq);
301			spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
302			dev_err(asd->isp->dev, "%s, css q fails: %d\n",
303				__func__, err);
304			return -EINVAL;
305		}
306
307		/* enqueue 3A/DIS/metadata buffers */
308		if (asd->params.curr_grid_info.s3a_grid.enable &&
309		    css_pipe_id == asd->params.s3a_enabled_pipe &&
310		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
311			atomisp_q_one_s3a_buffer(asd, stream_id,
312						 css_pipe_id);
313
314		if (asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info.
315		    metadata_info.size &&
316		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
317			atomisp_q_one_metadata_buffer(asd, stream_id,
318						      css_pipe_id);
319
320		if (dvs_grid && dvs_grid->enable &&
321		    css_pipe_id == IA_CSS_PIPE_ID_VIDEO &&
322		    css_buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME)
323			atomisp_q_one_dis_buffer(asd, stream_id,
324						 css_pipe_id);
325	}
326
327	return 0;
328}
329
330/* queue all available buffers to css */
331int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd)
332{
333	enum ia_css_pipe_id pipe_id;
334
335	if (asd->copy_mode) {
336		pipe_id = IA_CSS_PIPE_ID_COPY;
337	} else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) {
338		pipe_id = IA_CSS_PIPE_ID_VIDEO;
339	} else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) {
340		pipe_id = IA_CSS_PIPE_ID_CAPTURE;
341	} else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
342		pipe_id = IA_CSS_PIPE_ID_VIDEO;
343	} else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) {
344		pipe_id = IA_CSS_PIPE_ID_PREVIEW;
345	} else {
346		/* ATOMISP_RUN_MODE_STILL_CAPTURE */
347		pipe_id = IA_CSS_PIPE_ID_CAPTURE;
348	}
349
350	atomisp_q_video_buffers_to_css(asd, &asd->video_out,
351				       ATOMISP_INPUT_STREAM_GENERAL,
352				       IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, pipe_id);
353	return 0;
354}
355
356static void atomisp_buf_queue(struct vb2_buffer *vb)
357{
358	struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
359	struct ia_css_frame *frame = vb_to_frame(vb);
360	struct atomisp_sub_device *asd = pipe->asd;
361	unsigned long irqflags;
362	int ret;
363
364	mutex_lock(&asd->isp->mutex);
365
366	ret = atomisp_pipe_check(pipe, false);
367	if (ret) {
368		spin_lock_irqsave(&pipe->irq_lock, irqflags);
369		atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
370		spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
371		goto out_unlock;
372	}
373
374	/* FIXME this ugliness comes from the original atomisp buffer handling */
375	if (!(vb->skip_cache_sync_on_finish && vb->skip_cache_sync_on_prepare))
376		wbinvd();
377
378	pipe->frame_params[vb->index] = NULL;
379
380	spin_lock_irqsave(&pipe->irq_lock, irqflags);
381	/*
382	 * when a frame buffer meets following conditions, it should be put into
383	 * the waiting list:
384	 * 1.  It is not a main output frame, and it has a per-frame parameter
385	 *     to go with it.
386	 * 2.  It is not a main output frame, and the waiting buffer list is not
387	 *     empty, to keep the FIFO sequence of frame buffer processing, it
388	 *     is put to waiting list until previous per-frame parameter buffers
389	 *     get enqueued.
390	 */
391	if (pipe->frame_request_config_id[vb->index] ||
392	    !list_empty(&pipe->buffers_waiting_for_param))
393		list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param);
394	else
395		list_add_tail(&frame->queue, &pipe->activeq);
396
397	spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
398
399	/* TODO: do this better, not best way to queue to css */
400	if (asd->streaming) {
401		if (!list_empty(&pipe->buffers_waiting_for_param))
402			atomisp_handle_parameter_and_buffer(pipe);
403		else
404			atomisp_qbuffers_to_css(asd);
405	}
406
407out_unlock:
408	mutex_unlock(&asd->isp->mutex);
409}
410
411static void atomisp_buf_cleanup(struct vb2_buffer *vb)
412{
413	struct atomisp_video_pipe *pipe = vb_to_pipe(vb);
414	struct ia_css_frame *frame = vb_to_frame(vb);
415	int index = frame->vb.vb2_buf.index;
416
417	pipe->frame_request_config_id[index] = 0;
418	pipe->frame_params[index] = NULL;
419
420	hmm_free(frame->data);
421}
422
423const struct vb2_ops atomisp_vb2_ops = {
424	.queue_setup		= atomisp_queue_setup,
425	.buf_init		= atomisp_buf_init,
426	.buf_cleanup		= atomisp_buf_cleanup,
427	.buf_queue		= atomisp_buf_queue,
428	.start_streaming	= atomisp_start_streaming,
429	.stop_streaming		= atomisp_stop_streaming,
430};
431
432static void atomisp_dev_init_struct(struct atomisp_device *isp)
433{
434	isp->isp_fatal_error = false;
435
436	/*
437	 * For Merrifield, frequency is scalable.
438	 * After boot-up, the default frequency is 200MHz.
439	 */
440	isp->running_freq = ISP_FREQ_200MHZ;
441}
442
443static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd)
444{
445	memset(&asd->params.css_param, 0, sizeof(asd->params.css_param));
446	asd->params.color_effect = V4L2_COLORFX_NONE;
447	asd->params.bad_pixel_en = true;
448	asd->params.gdc_cac_en = false;
449	asd->params.video_dis_en = false;
450	asd->params.sc_en = false;
451	asd->params.fpn_en = false;
452	asd->params.xnr_en = false;
453	asd->params.false_color = 0;
454	asd->params.yuv_ds_en = 0;
455	/* s3a grid not enabled for any pipe */
456	asd->params.s3a_enabled_pipe = IA_CSS_PIPE_ID_NUM;
457
458	asd->copy_mode = false;
459
460	asd->stream_prepared = false;
461	asd->high_speed_mode = false;
462	asd->sensor_array_res.height = 0;
463	asd->sensor_array_res.width = 0;
464	atomisp_css_init_struct(asd);
465}
466
467/*
468 * file operation functions
469 */
470static int atomisp_open(struct file *file)
471{
472	struct video_device *vdev = video_devdata(file);
473	struct atomisp_device *isp = video_get_drvdata(vdev);
474	struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
475	struct atomisp_sub_device *asd = pipe->asd;
476	int ret;
477
478	dev_dbg(isp->dev, "open device %s\n", vdev->name);
479
480	ret = v4l2_fh_open(file);
481	if (ret)
482		return ret;
483
484	mutex_lock(&isp->mutex);
485
486	if (!isp->input_cnt) {
487		dev_err(isp->dev, "no camera attached\n");
488		ret = -EINVAL;
489		goto error;
490	}
491
492	/*
493	 * atomisp does not allow multiple open
494	 */
495	if (pipe->users) {
496		dev_dbg(isp->dev, "video node already opened\n");
497		ret = -EBUSY;
498		goto error;
499	}
500
501	/* runtime power management, turn on ISP */
502	ret = pm_runtime_resume_and_get(vdev->v4l2_dev->dev);
503	if (ret < 0) {
504		dev_err(isp->dev, "Failed to power on device\n");
505		goto error;
506	}
507
508	atomisp_dev_init_struct(isp);
509	atomisp_subdev_init_struct(asd);
510
511	pipe->users++;
512	mutex_unlock(&isp->mutex);
513	return 0;
514
515error:
516	mutex_unlock(&isp->mutex);
517	v4l2_fh_release(file);
518	return ret;
519}
520
521static int atomisp_release(struct file *file)
522{
523	struct video_device *vdev = video_devdata(file);
524	struct atomisp_device *isp = video_get_drvdata(vdev);
525	struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev);
526	struct atomisp_sub_device *asd = pipe->asd;
527	struct v4l2_subdev_fh fh;
528
529	v4l2_fh_init(&fh.vfh, vdev);
530
531	dev_dbg(isp->dev, "release device %s\n", vdev->name);
532
533	/* Note file must not be used after this! */
534	vb2_fop_release(file);
535
536	mutex_lock(&isp->mutex);
537
538	pipe->users--;
539
540	atomisp_css_free_stat_buffers(asd);
541	atomisp_free_internal_buffers(asd);
542
543	atomisp_s_sensor_power(isp, asd->input_curr, 0);
544
545	atomisp_destroy_pipes_stream(asd);
546
547	if (pm_runtime_put_sync(vdev->v4l2_dev->dev) < 0)
548		dev_err(isp->dev, "Failed to power off device\n");
549
550	mutex_unlock(&isp->mutex);
551	return 0;
552}
553
554const struct v4l2_file_operations atomisp_fops = {
555	.owner = THIS_MODULE,
556	.open = atomisp_open,
557	.release = atomisp_release,
558	.mmap = vb2_fop_mmap,
559	.poll = vb2_fop_poll,
560	.unlocked_ioctl = video_ioctl2,
561#ifdef CONFIG_COMPAT
562	/*
563	 * this was removed because of bugs, the interface
564	 * needs to be made safe for compat tasks instead.
565	.compat_ioctl32 = atomisp_compat_ioctl32,
566	 */
567#endif
568};
~/ Read-only file preview.