FILE
atomisp_fops.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/atomisp/pci/atomisp_fops.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 ¶m->params); 259 atomisp_apply_css_parameters(asd, ¶m->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 ¶m->params.dz_config, css_pipe_id); 265 if (!err) 266 asd->params.config.dz_config = ¶m->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 ¶m->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.