diff options
author | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2011-08-24 19:35:30 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-09-06 16:48:45 -0400 |
commit | 237e026559b7cd03fc575b6007cea11aef9e0aa6 (patch) | |
tree | 0d810b1d4a52973c497682fec2c9b817ecfca19e | |
parent | 4db5e27ed9401a635b3c10994f2971c0441e3c90 (diff) |
[media] s5p-fimc: Add subdev for the FIMC processing block
Add a subdev to expose the host's scaling and composition functions.
The camera frame composition onto an output buffer may be configured
through set/get_crop at FIMC.{n} source pad.
Additionally allow crop, composition and controls to be modified
during streaming. Make sure the default format is set when opening
the video capture node.
Rename struct fimc_vid_cap::fmt to more relevant 'mf' to avoid
confusion.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-capture.c | 734 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.c | 30 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-core.h | 53 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-mdevice.c | 47 | ||||
-rw-r--r-- | drivers/media/video/s5p-fimc/fimc-reg.c | 8 |
5 files changed, 751 insertions, 121 deletions
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index f0fed6124dc0..62fd8a17fd5f 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c | |||
@@ -63,6 +63,7 @@ static int fimc_init_capture(struct fimc_dev *fimc) | |||
63 | fimc_hw_set_effect(ctx); | 63 | fimc_hw_set_effect(ctx); |
64 | fimc_hw_set_output_path(ctx); | 64 | fimc_hw_set_output_path(ctx); |
65 | fimc_hw_set_out_dma(ctx); | 65 | fimc_hw_set_out_dma(ctx); |
66 | clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
66 | } | 67 | } |
67 | spin_unlock_irqrestore(&fimc->slock, flags); | 68 | spin_unlock_irqrestore(&fimc->slock, flags); |
68 | return ret; | 69 | return ret; |
@@ -120,6 +121,36 @@ static int fimc_stop_capture(struct fimc_dev *fimc) | |||
120 | return fimc_capture_state_cleanup(fimc); | 121 | return fimc_capture_state_cleanup(fimc); |
121 | } | 122 | } |
122 | 123 | ||
124 | /** | ||
125 | * fimc_capture_config_update - apply the camera interface configuration | ||
126 | * | ||
127 | * To be called from within the interrupt handler with fimc.slock | ||
128 | * spinlock held. It updates the camera pixel crop, rotation and | ||
129 | * image flip in H/W. | ||
130 | */ | ||
131 | int fimc_capture_config_update(struct fimc_ctx *ctx) | ||
132 | { | ||
133 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
134 | int ret; | ||
135 | |||
136 | if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) | ||
137 | return 0; | ||
138 | |||
139 | spin_lock(&ctx->slock); | ||
140 | fimc_hw_set_camera_offset(fimc, &ctx->s_frame); | ||
141 | ret = fimc_set_scaler_info(ctx); | ||
142 | if (ret == 0) { | ||
143 | fimc_hw_set_prescaler(ctx); | ||
144 | fimc_hw_set_mainscaler(ctx); | ||
145 | fimc_hw_set_target_format(ctx); | ||
146 | fimc_hw_set_rotation(ctx); | ||
147 | fimc_prepare_dma_offset(ctx, &ctx->d_frame); | ||
148 | fimc_hw_set_out_dma(ctx); | ||
149 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
150 | } | ||
151 | spin_unlock(&ctx->slock); | ||
152 | return ret; | ||
153 | } | ||
123 | 154 | ||
124 | static int start_streaming(struct vb2_queue *q, unsigned int count) | 155 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
125 | { | 156 | { |
@@ -319,6 +350,8 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) | |||
319 | fimc->pipeline.sensor->ctrl_handler); | 350 | fimc->pipeline.sensor->ctrl_handler); |
320 | } | 351 | } |
321 | 352 | ||
353 | static int fimc_capture_set_default_format(struct fimc_dev *fimc); | ||
354 | |||
322 | static int fimc_capture_open(struct file *file) | 355 | static int fimc_capture_open(struct file *file) |
323 | { | 356 | { |
324 | struct fimc_dev *fimc = video_drvdata(file); | 357 | struct fimc_dev *fimc = video_drvdata(file); |
@@ -347,6 +380,9 @@ static int fimc_capture_open(struct file *file) | |||
347 | return ret; | 380 | return ret; |
348 | } | 381 | } |
349 | ret = fimc_capture_ctrls_create(fimc); | 382 | ret = fimc_capture_ctrls_create(fimc); |
383 | |||
384 | if (!ret && !fimc->vid_cap.user_subdev_api) | ||
385 | ret = fimc_capture_set_default_format(fimc); | ||
350 | } | 386 | } |
351 | return ret; | 387 | return ret; |
352 | } | 388 | } |
@@ -384,7 +420,6 @@ static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma) | |||
384 | return vb2_mmap(&fimc->vid_cap.vbq, vma); | 420 | return vb2_mmap(&fimc->vid_cap.vbq, vma); |
385 | } | 421 | } |
386 | 422 | ||
387 | /* video device file operations */ | ||
388 | static const struct v4l2_file_operations fimc_capture_fops = { | 423 | static const struct v4l2_file_operations fimc_capture_fops = { |
389 | .owner = THIS_MODULE, | 424 | .owner = THIS_MODULE, |
390 | .open = fimc_capture_open, | 425 | .open = fimc_capture_open, |
@@ -394,6 +429,147 @@ static const struct v4l2_file_operations fimc_capture_fops = { | |||
394 | .mmap = fimc_capture_mmap, | 429 | .mmap = fimc_capture_mmap, |
395 | }; | 430 | }; |
396 | 431 | ||
432 | /* | ||
433 | * Format and crop negotiation helpers | ||
434 | */ | ||
435 | |||
436 | static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, | ||
437 | u32 *width, u32 *height, | ||
438 | u32 *code, u32 *fourcc, int pad) | ||
439 | { | ||
440 | bool rotation = ctx->rotation == 90 || ctx->rotation == 270; | ||
441 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
442 | struct samsung_fimc_variant *var = fimc->variant; | ||
443 | struct fimc_pix_limit *pl = var->pix_limit; | ||
444 | struct fimc_frame *dst = &ctx->d_frame; | ||
445 | u32 depth, min_w, max_w, min_h, align_h = 3; | ||
446 | u32 mask = FMT_FLAGS_CAM; | ||
447 | struct fimc_fmt *ffmt; | ||
448 | |||
449 | /* Color conversion from/to JPEG is not supported */ | ||
450 | if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE && | ||
451 | fimc_fmt_is_jpeg(ctx->s_frame.fmt->color)) | ||
452 | *code = V4L2_MBUS_FMT_JPEG_1X8; | ||
453 | |||
454 | if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK) | ||
455 | mask |= FMT_FLAGS_M2M; | ||
456 | |||
457 | ffmt = fimc_find_format(fourcc, code, mask, 0); | ||
458 | if (WARN_ON(!ffmt)) | ||
459 | return NULL; | ||
460 | if (code) | ||
461 | *code = ffmt->mbus_code; | ||
462 | if (fourcc) | ||
463 | *fourcc = ffmt->fourcc; | ||
464 | |||
465 | if (pad == FIMC_SD_PAD_SINK) { | ||
466 | max_w = fimc_fmt_is_jpeg(ffmt->color) ? | ||
467 | pl->scaler_dis_w : pl->scaler_en_w; | ||
468 | /* Apply the camera input interface pixel constraints */ | ||
469 | v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4, | ||
470 | height, max_t(u32, *height, 32), | ||
471 | FIMC_CAMIF_MAX_HEIGHT, | ||
472 | fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1, | ||
473 | 0); | ||
474 | return ffmt; | ||
475 | } | ||
476 | /* Can't scale or crop in transparent (JPEG) transfer mode */ | ||
477 | if (fimc_fmt_is_jpeg(ffmt->color)) { | ||
478 | *width = ctx->s_frame.f_width; | ||
479 | *height = ctx->s_frame.f_height; | ||
480 | return ffmt; | ||
481 | } | ||
482 | /* Apply the scaler and the output DMA constraints */ | ||
483 | max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; | ||
484 | min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize; | ||
485 | min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize; | ||
486 | if (fimc->id == 1 && var->pix_hoff) | ||
487 | align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; | ||
488 | |||
489 | depth = fimc_get_format_depth(ffmt); | ||
490 | v4l_bound_align_image(width, min_w, max_w, | ||
491 | ffs(var->min_out_pixsize) - 1, | ||
492 | height, min_h, FIMC_CAMIF_MAX_HEIGHT, | ||
493 | align_h, | ||
494 | 64/(ALIGN(depth, 8))); | ||
495 | |||
496 | dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d", | ||
497 | pad, code ? *code : 0, *width, *height, | ||
498 | dst->f_width, dst->f_height); | ||
499 | |||
500 | return ffmt; | ||
501 | } | ||
502 | |||
503 | static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, | ||
504 | int pad) | ||
505 | { | ||
506 | bool rotate = ctx->rotation == 90 || ctx->rotation == 270; | ||
507 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
508 | struct samsung_fimc_variant *var = fimc->variant; | ||
509 | struct fimc_pix_limit *pl = var->pix_limit; | ||
510 | struct fimc_frame *sink = &ctx->s_frame; | ||
511 | u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; | ||
512 | u32 align_sz = 0, align_h = 4; | ||
513 | u32 max_sc_h, max_sc_v; | ||
514 | |||
515 | /* In JPEG transparent transfer mode cropping is not supported */ | ||
516 | if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) { | ||
517 | r->width = sink->f_width; | ||
518 | r->height = sink->f_height; | ||
519 | r->left = r->top = 0; | ||
520 | return; | ||
521 | } | ||
522 | if (pad == FIMC_SD_PAD_SOURCE) { | ||
523 | if (ctx->rotation != 90 && ctx->rotation != 270) | ||
524 | align_h = 1; | ||
525 | max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); | ||
526 | max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1)); | ||
527 | min_sz = var->min_out_pixsize; | ||
528 | } else { | ||
529 | u32 depth = fimc_get_format_depth(sink->fmt); | ||
530 | align_sz = 64/ALIGN(depth, 8); | ||
531 | min_sz = var->min_inp_pixsize; | ||
532 | min_w = min_h = min_sz; | ||
533 | max_sc_h = max_sc_v = 1; | ||
534 | } | ||
535 | /* | ||
536 | * For the crop rectangle at source pad the following constraints | ||
537 | * must be met: | ||
538 | * - it must fit in the sink pad format rectangle (f_width/f_height); | ||
539 | * - maximum downscaling ratio is 64; | ||
540 | * - maximum crop size depends if the rotator is used or not; | ||
541 | * - the sink pad format width/height must be 4 multiple of the | ||
542 | * prescaler ratios determined by sink pad size and source pad crop, | ||
543 | * the prescaler ratio is returned by fimc_get_scaler_factor(). | ||
544 | */ | ||
545 | max_w = min_t(u32, | ||
546 | rotate ? pl->out_rot_en_w : pl->out_rot_dis_w, | ||
547 | rotate ? sink->f_height : sink->f_width); | ||
548 | max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); | ||
549 | if (pad == FIMC_SD_PAD_SOURCE) { | ||
550 | min_w = min_t(u32, max_w, sink->f_width / max_sc_h); | ||
551 | min_h = min_t(u32, max_h, sink->f_height / max_sc_v); | ||
552 | if (rotate) { | ||
553 | swap(max_sc_h, max_sc_v); | ||
554 | swap(min_w, min_h); | ||
555 | } | ||
556 | } | ||
557 | v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1, | ||
558 | &r->height, min_h, max_h, align_h, | ||
559 | align_sz); | ||
560 | /* Adjust left/top if cropping rectangle is out of bounds */ | ||
561 | r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width); | ||
562 | r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); | ||
563 | r->left = round_down(r->left, var->hor_offs_align); | ||
564 | |||
565 | dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d", | ||
566 | pad, r->left, r->top, r->width, r->height, | ||
567 | sink->f_width, sink->f_height); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * The video node ioctl operations | ||
572 | */ | ||
397 | static int fimc_vidioc_querycap_capture(struct file *file, void *priv, | 573 | static int fimc_vidioc_querycap_capture(struct file *file, void *priv, |
398 | struct v4l2_capability *cap) | 574 | struct v4l2_capability *cap) |
399 | { | 575 | { |
@@ -424,11 +600,81 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, | |||
424 | return 0; | 600 | return 0; |
425 | } | 601 | } |
426 | 602 | ||
603 | /** | ||
604 | * fimc_pipeline_try_format - negotiate and/or set formats at pipeline | ||
605 | * elements | ||
606 | * @ctx: FIMC capture context | ||
607 | * @tfmt: media bus format to try/set on subdevs | ||
608 | * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output) | ||
609 | * @set: true to set format on subdevs, false to try only | ||
610 | */ | ||
611 | static int fimc_pipeline_try_format(struct fimc_ctx *ctx, | ||
612 | struct v4l2_mbus_framefmt *tfmt, | ||
613 | struct fimc_fmt **fmt_id, | ||
614 | bool set) | ||
615 | { | ||
616 | struct fimc_dev *fimc = ctx->fimc_dev; | ||
617 | struct v4l2_subdev *sd = fimc->pipeline.sensor; | ||
618 | struct v4l2_subdev *csis = fimc->pipeline.csis; | ||
619 | struct v4l2_subdev_format sfmt; | ||
620 | struct v4l2_mbus_framefmt *mf = &sfmt.format; | ||
621 | struct fimc_fmt *ffmt = NULL; | ||
622 | int ret, i = 0; | ||
623 | |||
624 | if (WARN_ON(!sd || !tfmt)) | ||
625 | return -EINVAL; | ||
427 | 626 | ||
627 | memset(&sfmt, 0, sizeof(sfmt)); | ||
628 | sfmt.format = *tfmt; | ||
629 | |||
630 | sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; | ||
631 | while (1) { | ||
632 | ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, | ||
633 | FMT_FLAGS_CAM, i++); | ||
634 | if (ffmt == NULL) { | ||
635 | /* | ||
636 | * Notify user-space if common pixel code for | ||
637 | * host and sensor does not exist. | ||
638 | */ | ||
639 | return -EINVAL; | ||
640 | } | ||
641 | mf->code = tfmt->code = ffmt->mbus_code; | ||
428 | 642 | ||
643 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); | ||
644 | if (ret) | ||
645 | return ret; | ||
646 | if (mf->code != tfmt->code) { | ||
647 | mf->code = 0; | ||
648 | continue; | ||
649 | } | ||
650 | if (mf->width != tfmt->width || mf->width != tfmt->width) { | ||
651 | u32 fcc = ffmt->fourcc; | ||
652 | tfmt->width = mf->width; | ||
653 | tfmt->height = mf->height; | ||
654 | ffmt = fimc_capture_try_format(ctx, | ||
655 | &tfmt->width, &tfmt->height, | ||
656 | NULL, &fcc, FIMC_SD_PAD_SOURCE); | ||
657 | if (ffmt && ffmt->mbus_code) | ||
658 | mf->code = ffmt->mbus_code; | ||
659 | if (mf->width != tfmt->width || mf->width != tfmt->width) | ||
660 | continue; | ||
661 | tfmt->code = mf->code; | ||
662 | } | ||
663 | if (csis) | ||
664 | ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); | ||
429 | 665 | ||
666 | if (mf->code == tfmt->code && | ||
667 | mf->width == tfmt->width && mf->width == tfmt->width) | ||
668 | break; | ||
669 | } | ||
430 | 670 | ||
671 | if (fmt_id && ffmt) | ||
672 | *fmt_id = ffmt; | ||
673 | *tfmt = *mf; | ||
431 | 674 | ||
675 | dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); | ||
676 | return 0; | ||
677 | } | ||
432 | 678 | ||
433 | static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, | 679 | static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, |
434 | struct v4l2_format *f) | 680 | struct v4l2_format *f) |
@@ -445,55 +691,114 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, | |||
445 | static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, | 691 | static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, |
446 | struct v4l2_format *f) | 692 | struct v4l2_format *f) |
447 | { | 693 | { |
694 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
448 | struct fimc_dev *fimc = video_drvdata(file); | 695 | struct fimc_dev *fimc = video_drvdata(file); |
449 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | 696 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; |
697 | struct v4l2_mbus_framefmt mf; | ||
698 | struct fimc_fmt *ffmt = NULL; | ||
699 | |||
700 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
701 | return -EINVAL; | ||
702 | |||
703 | if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { | ||
704 | fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
705 | NULL, &pix->pixelformat, | ||
706 | FIMC_SD_PAD_SINK); | ||
707 | ctx->s_frame.f_width = pix->width; | ||
708 | ctx->s_frame.f_height = pix->height; | ||
709 | } | ||
710 | ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
711 | NULL, &pix->pixelformat, | ||
712 | FIMC_SD_PAD_SOURCE); | ||
713 | if (!ffmt) | ||
714 | return -EINVAL; | ||
715 | |||
716 | if (!fimc->vid_cap.user_subdev_api) { | ||
717 | mf.width = pix->width; | ||
718 | mf.height = pix->height; | ||
719 | mf.code = ffmt->mbus_code; | ||
720 | fimc_md_graph_lock(fimc); | ||
721 | fimc_pipeline_try_format(ctx, &mf, &ffmt, false); | ||
722 | fimc_md_graph_unlock(fimc); | ||
723 | |||
724 | pix->width = mf.width; | ||
725 | pix->height = mf.height; | ||
726 | if (ffmt) | ||
727 | pix->pixelformat = ffmt->fourcc; | ||
728 | } | ||
450 | 729 | ||
730 | fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix); | ||
451 | return 0; | 731 | return 0; |
452 | } | 732 | } |
453 | 733 | ||
454 | static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, | 734 | static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) |
455 | struct v4l2_format *f) | ||
456 | { | 735 | { |
457 | struct fimc_dev *fimc = video_drvdata(file); | ||
458 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | 736 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; |
459 | struct v4l2_pix_format_mplane *pix; | 737 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; |
460 | struct fimc_frame *frame; | 738 | struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf; |
461 | int ret; | 739 | struct fimc_frame *ff = &ctx->d_frame; |
462 | int i; | 740 | struct fimc_fmt *s_fmt = NULL; |
741 | int ret, i; | ||
463 | 742 | ||
464 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | 743 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) |
465 | return -EINVAL; | 744 | return -EINVAL; |
466 | 745 | if (vb2_is_busy(&fimc->vid_cap.vbq)) | |
467 | if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc)) | ||
468 | return -EBUSY; | 746 | return -EBUSY; |
469 | 747 | ||
470 | frame = &ctx->d_frame; | 748 | /* Pre-configure format at camera interface input, for JPEG only */ |
471 | 749 | if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { | |
472 | pix = &f->fmt.pix_mp; | 750 | fimc_capture_try_format(ctx, &pix->width, &pix->height, |
473 | frame->fmt = fimc_find_format(&pix->pixelformat, NULL, | 751 | NULL, &pix->pixelformat, |
474 | FMT_FLAGS_M2M | FMT_FLAGS_CAM, 0); | 752 | FIMC_SD_PAD_SINK); |
475 | if (WARN(frame->fmt == NULL, "Pixel format lookup failed\n")) | 753 | ctx->s_frame.f_width = pix->width; |
754 | ctx->s_frame.f_height = pix->height; | ||
755 | } | ||
756 | /* Try the format at the scaler and the DMA output */ | ||
757 | ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, | ||
758 | NULL, &pix->pixelformat, | ||
759 | FIMC_SD_PAD_SOURCE); | ||
760 | if (!ff->fmt) | ||
476 | return -EINVAL; | 761 | return -EINVAL; |
477 | 762 | /* Try to match format at the host and the sensor */ | |
478 | for (i = 0; i < frame->fmt->colplanes; i++) { | 763 | if (!fimc->vid_cap.user_subdev_api) { |
479 | frame->payload[i] = | 764 | mf->code = ff->fmt->mbus_code; |
480 | (pix->width * pix->height * frame->fmt->depth[i]) >> 3; | 765 | mf->width = pix->width; |
766 | mf->height = pix->height; | ||
767 | |||
768 | fimc_md_graph_lock(fimc); | ||
769 | ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); | ||
770 | fimc_md_graph_unlock(fimc); | ||
771 | if (ret) | ||
772 | return ret; | ||
773 | pix->width = mf->width; | ||
774 | pix->height = mf->height; | ||
775 | } | ||
776 | fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); | ||
777 | for (i = 0; i < ff->fmt->colplanes; i++) | ||
778 | ff->payload[i] = | ||
779 | (pix->width * pix->height * ff->fmt->depth[i]) / 8; | ||
780 | |||
781 | set_frame_bounds(ff, pix->width, pix->height); | ||
782 | /* Reset the composition rectangle if not yet configured */ | ||
783 | if (!(ctx->state & FIMC_DST_CROP)) | ||
784 | set_frame_crop(ff, 0, 0, pix->width, pix->height); | ||
785 | |||
786 | /* Reset cropping and set format at the camera interface input */ | ||
787 | if (!fimc->vid_cap.user_subdev_api) { | ||
788 | ctx->s_frame.fmt = s_fmt; | ||
789 | set_frame_bounds(&ctx->s_frame, pix->width, pix->height); | ||
790 | set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height); | ||
481 | } | 791 | } |
482 | 792 | ||
483 | /* Output DMA frame pixel size and offsets. */ | 793 | return ret; |
484 | frame->f_width = pix->plane_fmt[0].bytesperline * 8 | 794 | } |
485 | / frame->fmt->depth[0]; | ||
486 | frame->f_height = pix->height; | ||
487 | frame->width = pix->width; | ||
488 | frame->height = pix->height; | ||
489 | frame->o_width = pix->width; | ||
490 | frame->o_height = pix->height; | ||
491 | frame->offs_h = 0; | ||
492 | frame->offs_v = 0; | ||
493 | 795 | ||
494 | ctx->state |= (FIMC_PARAMS | FIMC_DST_FMT); | 796 | static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, |
797 | struct v4l2_format *f) | ||
798 | { | ||
799 | struct fimc_dev *fimc = video_drvdata(file); | ||
495 | 800 | ||
496 | return ret; | 801 | return fimc_capture_set_format(fimc, f); |
497 | } | 802 | } |
498 | 803 | ||
499 | static int fimc_cap_enum_input(struct file *file, void *priv, | 804 | static int fimc_cap_enum_input(struct file *file, void *priv, |
@@ -522,22 +827,83 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i) | |||
522 | return 0; | 827 | return 0; |
523 | } | 828 | } |
524 | 829 | ||
830 | /** | ||
831 | * fimc_pipeline_validate - check for formats inconsistencies | ||
832 | * between source and sink pad of each link | ||
833 | * | ||
834 | * Return 0 if all formats match or -EPIPE otherwise. | ||
835 | */ | ||
836 | static int fimc_pipeline_validate(struct fimc_dev *fimc) | ||
837 | { | ||
838 | struct v4l2_subdev_format sink_fmt, src_fmt; | ||
839 | struct fimc_vid_cap *vid_cap = &fimc->vid_cap; | ||
840 | struct v4l2_subdev *sd; | ||
841 | struct media_pad *pad; | ||
842 | int ret; | ||
843 | |||
844 | /* Start with the video capture node pad */ | ||
845 | pad = media_entity_remote_source(&vid_cap->vd_pad); | ||
846 | if (pad == NULL) | ||
847 | return -EPIPE; | ||
848 | /* FIMC.{N} subdevice */ | ||
849 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
850 | |||
851 | while (1) { | ||
852 | /* Retrieve format at the sink pad */ | ||
853 | pad = &sd->entity.pads[0]; | ||
854 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
855 | break; | ||
856 | /* Don't call FIMC subdev operation to avoid nested locking */ | ||
857 | if (sd == fimc->vid_cap.subdev) { | ||
858 | struct fimc_frame *ff = &vid_cap->ctx->s_frame; | ||
859 | sink_fmt.format.width = ff->f_width; | ||
860 | sink_fmt.format.height = ff->f_height; | ||
861 | sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; | ||
862 | } else { | ||
863 | sink_fmt.pad = pad->index; | ||
864 | sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
865 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); | ||
866 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
867 | return -EPIPE; | ||
868 | } | ||
869 | /* Retrieve format at the source pad */ | ||
870 | pad = media_entity_remote_source(pad); | ||
871 | if (pad == NULL || | ||
872 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
873 | break; | ||
874 | |||
875 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
876 | src_fmt.pad = pad->index; | ||
877 | src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
878 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); | ||
879 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
880 | return -EPIPE; | ||
881 | |||
882 | if (src_fmt.format.width != sink_fmt.format.width || | ||
883 | src_fmt.format.height != sink_fmt.format.height || | ||
884 | src_fmt.format.code != sink_fmt.format.code) | ||
885 | return -EPIPE; | ||
886 | } | ||
887 | return 0; | ||
888 | } | ||
889 | |||
525 | static int fimc_cap_streamon(struct file *file, void *priv, | 890 | static int fimc_cap_streamon(struct file *file, void *priv, |
526 | enum v4l2_buf_type type) | 891 | enum v4l2_buf_type type) |
527 | { | 892 | { |
528 | struct fimc_dev *fimc = video_drvdata(file); | 893 | struct fimc_dev *fimc = video_drvdata(file); |
529 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
530 | struct fimc_pipeline *p = &fimc->pipeline; | 894 | struct fimc_pipeline *p = &fimc->pipeline; |
895 | int ret; | ||
531 | 896 | ||
532 | if (fimc_capture_active(fimc)) | 897 | if (fimc_capture_active(fimc)) |
533 | return -EBUSY; | 898 | return -EBUSY; |
534 | 899 | ||
535 | if (!(ctx->state & FIMC_DST_FMT)) { | ||
536 | v4l2_err(fimc->vid_cap.vfd, "Format is not set\n"); | ||
537 | return -EINVAL; | ||
538 | } | ||
539 | media_entity_pipeline_start(&p->sensor->entity, p->pipe); | 900 | media_entity_pipeline_start(&p->sensor->entity, p->pipe); |
540 | 901 | ||
902 | if (fimc->vid_cap.user_subdev_api) { | ||
903 | ret = fimc_pipeline_validate(fimc); | ||
904 | if (ret) | ||
905 | return ret; | ||
906 | } | ||
541 | return vb2_streamon(&fimc->vid_cap.vbq, type); | 907 | return vb2_streamon(&fimc->vid_cap.vbq, type); |
542 | } | 908 | } |
543 | 909 | ||
@@ -624,35 +990,16 @@ static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | |||
624 | { | 990 | { |
625 | struct fimc_dev *fimc = video_drvdata(file); | 991 | struct fimc_dev *fimc = video_drvdata(file); |
626 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | 992 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; |
627 | struct fimc_frame *f; | 993 | struct fimc_frame *ff; |
628 | int ret = -EINVAL; | 994 | unsigned long flags; |
629 | |||
630 | if (fimc_capture_active(fimc)) | ||
631 | return -EBUSY; | ||
632 | |||
633 | ret = fimc_try_crop(ctx, cr); | ||
634 | if (ret) | ||
635 | return ret; | ||
636 | |||
637 | if (!(ctx->state & FIMC_DST_FMT)) { | ||
638 | v4l2_err(fimc->vid_cap.vfd, "Capture format is not set\n"); | ||
639 | return -EINVAL; | ||
640 | } | ||
641 | 995 | ||
642 | f = &ctx->s_frame; | 996 | fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK); |
643 | /* Check for the pixel scaling ratio when cropping input image. */ | 997 | ff = &ctx->s_frame; |
644 | ret = fimc_check_scaler_ratio(cr->c.width, cr->c.height, | ||
645 | ctx->d_frame.width, ctx->d_frame.height, | ||
646 | ctx->rotation); | ||
647 | if (ret) { | ||
648 | v4l2_err(fimc->vid_cap.vfd, "Out of the scaler range\n"); | ||
649 | return ret; | ||
650 | } | ||
651 | 998 | ||
652 | f->offs_h = cr->c.left; | 999 | spin_lock_irqsave(&fimc->slock, flags); |
653 | f->offs_v = cr->c.top; | 1000 | set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height); |
654 | f->width = cr->c.width; | 1001 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); |
655 | f->height = cr->c.height; | 1002 | spin_unlock_irqrestore(&fimc->slock, flags); |
656 | 1003 | ||
657 | return 0; | 1004 | return 0; |
658 | } | 1005 | } |
@@ -683,14 +1030,16 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { | |||
683 | .vidioc_g_input = fimc_cap_g_input, | 1030 | .vidioc_g_input = fimc_cap_g_input, |
684 | }; | 1031 | }; |
685 | 1032 | ||
686 | /* Media operations */ | 1033 | /* Capture subdev media entity operations */ |
687 | static int fimc_link_setup(struct media_entity *entity, | 1034 | static int fimc_link_setup(struct media_entity *entity, |
688 | const struct media_pad *local, | 1035 | const struct media_pad *local, |
689 | const struct media_pad *remote, u32 flags) | 1036 | const struct media_pad *remote, u32 flags) |
690 | { | 1037 | { |
691 | struct video_device *vd = media_entity_to_video_device(entity); | 1038 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); |
692 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(remote->entity); | 1039 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); |
693 | struct fimc_dev *fimc = video_get_drvdata(vd); | 1040 | |
1041 | if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
1042 | return -EINVAL; | ||
694 | 1043 | ||
695 | if (WARN_ON(fimc == NULL)) | 1044 | if (WARN_ON(fimc == NULL)) |
696 | return 0; | 1045 | return 0; |
@@ -710,10 +1059,241 @@ static int fimc_link_setup(struct media_entity *entity, | |||
710 | return 0; | 1059 | return 0; |
711 | } | 1060 | } |
712 | 1061 | ||
713 | static const struct media_entity_operations fimc_media_ops = { | 1062 | static const struct media_entity_operations fimc_sd_media_ops = { |
714 | .link_setup = fimc_link_setup, | 1063 | .link_setup = fimc_link_setup, |
715 | }; | 1064 | }; |
716 | 1065 | ||
1066 | static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd, | ||
1067 | struct v4l2_subdev_fh *fh, | ||
1068 | struct v4l2_subdev_mbus_code_enum *code) | ||
1069 | { | ||
1070 | struct fimc_fmt *fmt; | ||
1071 | |||
1072 | fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index); | ||
1073 | if (!fmt) | ||
1074 | return -EINVAL; | ||
1075 | code->code = fmt->mbus_code; | ||
1076 | return 0; | ||
1077 | } | ||
1078 | |||
1079 | static int fimc_subdev_get_fmt(struct v4l2_subdev *sd, | ||
1080 | struct v4l2_subdev_fh *fh, | ||
1081 | struct v4l2_subdev_format *fmt) | ||
1082 | { | ||
1083 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1084 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1085 | struct v4l2_mbus_framefmt *mf; | ||
1086 | struct fimc_frame *ff; | ||
1087 | |||
1088 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1089 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1090 | fmt->format = *mf; | ||
1091 | return 0; | ||
1092 | } | ||
1093 | mf = &fmt->format; | ||
1094 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1095 | ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame; | ||
1096 | |||
1097 | mutex_lock(&fimc->lock); | ||
1098 | /* The pixel code is same on both input and output pad */ | ||
1099 | if (!WARN_ON(ctx->s_frame.fmt == NULL)) | ||
1100 | mf->code = ctx->s_frame.fmt->mbus_code; | ||
1101 | mf->width = ff->f_width; | ||
1102 | mf->height = ff->f_height; | ||
1103 | mutex_unlock(&fimc->lock); | ||
1104 | |||
1105 | return 0; | ||
1106 | } | ||
1107 | |||
1108 | static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, | ||
1109 | struct v4l2_subdev_fh *fh, | ||
1110 | struct v4l2_subdev_format *fmt) | ||
1111 | { | ||
1112 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1113 | struct v4l2_mbus_framefmt *mf = &fmt->format; | ||
1114 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1115 | struct fimc_frame *ff; | ||
1116 | struct fimc_fmt *ffmt; | ||
1117 | |||
1118 | dbg("pad%d: code: 0x%x, %dx%d", | ||
1119 | fmt->pad, mf->code, mf->width, mf->height); | ||
1120 | |||
1121 | if (fmt->pad == FIMC_SD_PAD_SOURCE && | ||
1122 | vb2_is_busy(&fimc->vid_cap.vbq)) | ||
1123 | return -EBUSY; | ||
1124 | |||
1125 | mutex_lock(&fimc->lock); | ||
1126 | ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height, | ||
1127 | &mf->code, NULL, fmt->pad); | ||
1128 | mutex_unlock(&fimc->lock); | ||
1129 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
1130 | |||
1131 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1132 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1133 | *mf = fmt->format; | ||
1134 | return 0; | ||
1135 | } | ||
1136 | ff = fmt->pad == FIMC_SD_PAD_SINK ? | ||
1137 | &ctx->s_frame : &ctx->d_frame; | ||
1138 | |||
1139 | mutex_lock(&fimc->lock); | ||
1140 | set_frame_bounds(ff, mf->width, mf->height); | ||
1141 | ff->fmt = ffmt; | ||
1142 | |||
1143 | /* Reset the crop rectangle if required. */ | ||
1144 | if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP))) | ||
1145 | set_frame_crop(ff, 0, 0, mf->width, mf->height); | ||
1146 | |||
1147 | if (fmt->pad == FIMC_SD_PAD_SINK) | ||
1148 | ctx->state &= ~FIMC_DST_CROP; | ||
1149 | mutex_unlock(&fimc->lock); | ||
1150 | return 0; | ||
1151 | } | ||
1152 | |||
1153 | static int fimc_subdev_get_crop(struct v4l2_subdev *sd, | ||
1154 | struct v4l2_subdev_fh *fh, | ||
1155 | struct v4l2_subdev_crop *crop) | ||
1156 | { | ||
1157 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1158 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1159 | struct v4l2_rect *r = &crop->rect; | ||
1160 | struct fimc_frame *ff; | ||
1161 | |||
1162 | if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1163 | crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad); | ||
1164 | return 0; | ||
1165 | } | ||
1166 | ff = crop->pad == FIMC_SD_PAD_SINK ? | ||
1167 | &ctx->s_frame : &ctx->d_frame; | ||
1168 | |||
1169 | mutex_lock(&fimc->lock); | ||
1170 | r->left = ff->offs_h; | ||
1171 | r->top = ff->offs_v; | ||
1172 | r->width = ff->width; | ||
1173 | r->height = ff->height; | ||
1174 | mutex_unlock(&fimc->lock); | ||
1175 | |||
1176 | dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", | ||
1177 | ff, crop->pad, r->left, r->top, r->width, r->height, | ||
1178 | ff->f_width, ff->f_height); | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | static int fimc_subdev_set_crop(struct v4l2_subdev *sd, | ||
1184 | struct v4l2_subdev_fh *fh, | ||
1185 | struct v4l2_subdev_crop *crop) | ||
1186 | { | ||
1187 | struct fimc_dev *fimc = v4l2_get_subdevdata(sd); | ||
1188 | struct fimc_ctx *ctx = fimc->vid_cap.ctx; | ||
1189 | struct v4l2_rect *r = &crop->rect; | ||
1190 | struct fimc_frame *ff; | ||
1191 | unsigned long flags; | ||
1192 | |||
1193 | dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height); | ||
1194 | |||
1195 | ff = crop->pad == FIMC_SD_PAD_SOURCE ? | ||
1196 | &ctx->d_frame : &ctx->s_frame; | ||
1197 | |||
1198 | mutex_lock(&fimc->lock); | ||
1199 | fimc_capture_try_crop(ctx, r, crop->pad); | ||
1200 | |||
1201 | if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1202 | mutex_lock(&fimc->lock); | ||
1203 | *v4l2_subdev_get_try_crop(fh, crop->pad) = *r; | ||
1204 | return 0; | ||
1205 | } | ||
1206 | spin_lock_irqsave(&fimc->slock, flags); | ||
1207 | set_frame_crop(ff, r->left, r->top, r->width, r->height); | ||
1208 | if (crop->pad == FIMC_SD_PAD_SOURCE) | ||
1209 | ctx->state |= FIMC_DST_CROP; | ||
1210 | |||
1211 | set_bit(ST_CAPT_APPLY_CFG, &fimc->state); | ||
1212 | spin_unlock_irqrestore(&fimc->slock, flags); | ||
1213 | |||
1214 | dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top, | ||
1215 | r->width, r->height); | ||
1216 | |||
1217 | mutex_unlock(&fimc->lock); | ||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = { | ||
1222 | .enum_mbus_code = fimc_subdev_enum_mbus_code, | ||
1223 | .get_fmt = fimc_subdev_get_fmt, | ||
1224 | .set_fmt = fimc_subdev_set_fmt, | ||
1225 | .get_crop = fimc_subdev_get_crop, | ||
1226 | .set_crop = fimc_subdev_set_crop, | ||
1227 | }; | ||
1228 | |||
1229 | static struct v4l2_subdev_ops fimc_subdev_ops = { | ||
1230 | .pad = &fimc_subdev_pad_ops, | ||
1231 | }; | ||
1232 | |||
1233 | static int fimc_create_capture_subdev(struct fimc_dev *fimc, | ||
1234 | struct v4l2_device *v4l2_dev) | ||
1235 | { | ||
1236 | struct v4l2_subdev *sd; | ||
1237 | int ret; | ||
1238 | |||
1239 | sd = kzalloc(sizeof(*sd), GFP_KERNEL); | ||
1240 | if (!sd) | ||
1241 | return -ENOMEM; | ||
1242 | |||
1243 | v4l2_subdev_init(sd, &fimc_subdev_ops); | ||
1244 | sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1245 | snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); | ||
1246 | |||
1247 | fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1248 | fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1249 | ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, | ||
1250 | fimc->vid_cap.sd_pads, 0); | ||
1251 | if (ret) | ||
1252 | goto me_err; | ||
1253 | ret = v4l2_device_register_subdev(v4l2_dev, sd); | ||
1254 | if (ret) | ||
1255 | goto sd_err; | ||
1256 | |||
1257 | fimc->vid_cap.subdev = sd; | ||
1258 | v4l2_set_subdevdata(sd, fimc); | ||
1259 | sd->entity.ops = &fimc_sd_media_ops; | ||
1260 | return 0; | ||
1261 | sd_err: | ||
1262 | media_entity_cleanup(&sd->entity); | ||
1263 | me_err: | ||
1264 | kfree(sd); | ||
1265 | return ret; | ||
1266 | } | ||
1267 | |||
1268 | static void fimc_destroy_capture_subdev(struct fimc_dev *fimc) | ||
1269 | { | ||
1270 | struct v4l2_subdev *sd = fimc->vid_cap.subdev; | ||
1271 | |||
1272 | if (!sd) | ||
1273 | return; | ||
1274 | media_entity_cleanup(&sd->entity); | ||
1275 | v4l2_device_unregister_subdev(sd); | ||
1276 | kfree(sd); | ||
1277 | sd = NULL; | ||
1278 | } | ||
1279 | |||
1280 | /* Set default format at the sensor and host interface */ | ||
1281 | static int fimc_capture_set_default_format(struct fimc_dev *fimc) | ||
1282 | { | ||
1283 | struct v4l2_format fmt = { | ||
1284 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, | ||
1285 | .fmt.pix_mp = { | ||
1286 | .width = 640, | ||
1287 | .height = 480, | ||
1288 | .pixelformat = V4L2_PIX_FMT_YUYV, | ||
1289 | .field = V4L2_FIELD_NONE, | ||
1290 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
1291 | }, | ||
1292 | }; | ||
1293 | |||
1294 | return fimc_capture_set_format(fimc, &fmt); | ||
1295 | } | ||
1296 | |||
717 | /* fimc->lock must be already initialized */ | 1297 | /* fimc->lock must be already initialized */ |
718 | int fimc_register_capture_device(struct fimc_dev *fimc, | 1298 | int fimc_register_capture_device(struct fimc_dev *fimc, |
719 | struct v4l2_device *v4l2_dev) | 1299 | struct v4l2_device *v4l2_dev) |
@@ -721,7 +1301,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc, | |||
721 | struct video_device *vfd; | 1301 | struct video_device *vfd; |
722 | struct fimc_vid_cap *vid_cap; | 1302 | struct fimc_vid_cap *vid_cap; |
723 | struct fimc_ctx *ctx; | 1303 | struct fimc_ctx *ctx; |
724 | struct fimc_frame *fr; | ||
725 | struct vb2_queue *q; | 1304 | struct vb2_queue *q; |
726 | int ret = -ENOMEM; | 1305 | int ret = -ENOMEM; |
727 | 1306 | ||
@@ -733,12 +1312,8 @@ int fimc_register_capture_device(struct fimc_dev *fimc, | |||
733 | ctx->in_path = FIMC_CAMERA; | 1312 | ctx->in_path = FIMC_CAMERA; |
734 | ctx->out_path = FIMC_DMA; | 1313 | ctx->out_path = FIMC_DMA; |
735 | ctx->state = FIMC_CTX_CAP; | 1314 | ctx->state = FIMC_CTX_CAP; |
736 | 1315 | ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); | |
737 | /* Default format of the output frames */ | 1316 | ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); |
738 | fr = &ctx->d_frame; | ||
739 | fr->fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); | ||
740 | fr->width = fr->f_width = fr->o_width = 640; | ||
741 | fr->height = fr->f_height = fr->o_height = 480; | ||
742 | 1317 | ||
743 | vfd = video_device_alloc(); | 1318 | vfd = video_device_alloc(); |
744 | if (!vfd) { | 1319 | if (!vfd) { |
@@ -783,11 +1358,15 @@ int fimc_register_capture_device(struct fimc_dev *fimc, | |||
783 | ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0); | 1358 | ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0); |
784 | if (ret) | 1359 | if (ret) |
785 | goto err_ent; | 1360 | goto err_ent; |
1361 | ret = fimc_create_capture_subdev(fimc, v4l2_dev); | ||
1362 | if (ret) | ||
1363 | goto err_sd_reg; | ||
786 | 1364 | ||
787 | vfd->entity.ops = &fimc_media_ops; | ||
788 | vfd->ctrl_handler = &ctx->ctrl_handler; | 1365 | vfd->ctrl_handler = &ctx->ctrl_handler; |
789 | return 0; | 1366 | return 0; |
790 | 1367 | ||
1368 | err_sd_reg: | ||
1369 | media_entity_cleanup(&vfd->entity); | ||
791 | err_ent: | 1370 | err_ent: |
792 | video_device_release(vfd); | 1371 | video_device_release(vfd); |
793 | err_vd_alloc: | 1372 | err_vd_alloc: |
@@ -805,6 +1384,7 @@ void fimc_unregister_capture_device(struct fimc_dev *fimc) | |||
805 | not registered */ | 1384 | not registered */ |
806 | video_unregister_device(vfd); | 1385 | video_unregister_device(vfd); |
807 | } | 1386 | } |
1387 | fimc_destroy_capture_subdev(fimc); | ||
808 | kfree(fimc->vid_cap.ctx); | 1388 | kfree(fimc->vid_cap.ctx); |
809 | fimc->vid_cap.ctx = NULL; | 1389 | fimc->vid_cap.ctx = NULL; |
810 | } | 1390 | } |
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 605eb46e44d5..1a479a23cefb 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c | |||
@@ -372,6 +372,8 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc) | |||
372 | set_bit(ST_CAPT_RUN, &fimc->state); | 372 | set_bit(ST_CAPT_RUN, &fimc->state); |
373 | } | 373 | } |
374 | 374 | ||
375 | fimc_capture_config_update(cap->ctx); | ||
376 | |||
375 | dbg("frame: %d, active_buf_cnt: %d", | 377 | dbg("frame: %d, active_buf_cnt: %d", |
376 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); | 378 | fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); |
377 | } | 379 | } |
@@ -1198,12 +1200,11 @@ static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) | |||
1198 | return 0; | 1200 | return 0; |
1199 | } | 1201 | } |
1200 | 1202 | ||
1201 | int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | 1203 | static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) |
1202 | { | 1204 | { |
1203 | struct fimc_dev *fimc = ctx->fimc_dev; | 1205 | struct fimc_dev *fimc = ctx->fimc_dev; |
1204 | struct fimc_frame *f; | 1206 | struct fimc_frame *f; |
1205 | u32 min_size, halign, depth = 0; | 1207 | u32 min_size, halign, depth = 0; |
1206 | bool is_capture_ctx; | ||
1207 | int i; | 1208 | int i; |
1208 | 1209 | ||
1209 | if (cr->c.top < 0 || cr->c.left < 0) { | 1210 | if (cr->c.top < 0 || cr->c.left < 0) { |
@@ -1211,13 +1212,9 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | |||
1211 | "doesn't support negative values for top & left\n"); | 1212 | "doesn't support negative values for top & left\n"); |
1212 | return -EINVAL; | 1213 | return -EINVAL; |
1213 | } | 1214 | } |
1214 | |||
1215 | is_capture_ctx = fimc_ctx_state_is_set(FIMC_CTX_CAP, ctx); | ||
1216 | |||
1217 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | 1215 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) |
1218 | f = is_capture_ctx ? &ctx->s_frame : &ctx->d_frame; | 1216 | f = &ctx->d_frame; |
1219 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && | 1217 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) |
1220 | !is_capture_ctx) | ||
1221 | f = &ctx->s_frame; | 1218 | f = &ctx->s_frame; |
1222 | else | 1219 | else |
1223 | return -EINVAL; | 1220 | return -EINVAL; |
@@ -1226,15 +1223,10 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | |||
1226 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; | 1223 | fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; |
1227 | 1224 | ||
1228 | /* Get pixel alignment constraints. */ | 1225 | /* Get pixel alignment constraints. */ |
1229 | if (is_capture_ctx) { | 1226 | if (fimc->id == 1 && fimc->variant->pix_hoff) |
1230 | min_size = 16; | 1227 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; |
1231 | halign = 4; | 1228 | else |
1232 | } else { | 1229 | halign = ffs(min_size) - 1; |
1233 | if (fimc->id == 1 && fimc->variant->pix_hoff) | ||
1234 | halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; | ||
1235 | else | ||
1236 | halign = ffs(min_size) - 1; | ||
1237 | } | ||
1238 | 1230 | ||
1239 | for (i = 0; i < f->fmt->colplanes; i++) | 1231 | for (i = 0; i < f->fmt->colplanes; i++) |
1240 | depth += f->fmt->depth[i]; | 1232 | depth += f->fmt->depth[i]; |
@@ -1251,7 +1243,7 @@ int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) | |||
1251 | cr->c.top = f->o_height - cr->c.height; | 1243 | cr->c.top = f->o_height - cr->c.height; |
1252 | 1244 | ||
1253 | cr->c.left = round_down(cr->c.left, min_size); | 1245 | cr->c.left = round_down(cr->c.left, min_size); |
1254 | cr->c.top = round_down(cr->c.top, is_capture_ctx ? 16 : 8); | 1246 | cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); |
1255 | 1247 | ||
1256 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | 1248 | dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", |
1257 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, | 1249 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, |
@@ -1267,7 +1259,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) | |||
1267 | struct fimc_frame *f; | 1259 | struct fimc_frame *f; |
1268 | int ret; | 1260 | int ret; |
1269 | 1261 | ||
1270 | ret = fimc_try_crop(ctx, cr); | 1262 | ret = fimc_m2m_try_crop(ctx, cr); |
1271 | if (ret) | 1263 | if (ret) |
1272 | return ret; | 1264 | return ret; |
1273 | 1265 | ||
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 2935068fda2d..e4520b2dde43 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h | |||
@@ -43,6 +43,7 @@ | |||
43 | #define SCALER_MAX_HRATIO 64 | 43 | #define SCALER_MAX_HRATIO 64 |
44 | #define SCALER_MAX_VRATIO 64 | 44 | #define SCALER_MAX_VRATIO 64 |
45 | #define DMA_MIN_SIZE 8 | 45 | #define DMA_MIN_SIZE 8 |
46 | #define FIMC_CAMIF_MAX_HEIGHT 0x2000 | ||
46 | 47 | ||
47 | /* indices to the clocks array */ | 48 | /* indices to the clocks array */ |
48 | enum { | 49 | enum { |
@@ -92,9 +93,11 @@ enum fimc_color_fmt { | |||
92 | S5P_FIMC_CBYCRY422, | 93 | S5P_FIMC_CBYCRY422, |
93 | S5P_FIMC_CRYCBY422, | 94 | S5P_FIMC_CRYCBY422, |
94 | S5P_FIMC_YCBCR444_LOCAL, | 95 | S5P_FIMC_YCBCR444_LOCAL, |
96 | S5P_FIMC_JPEG = 0x40, | ||
95 | }; | 97 | }; |
96 | 98 | ||
97 | #define fimc_fmt_is_rgb(x) ((x) & 0x10) | 99 | #define fimc_fmt_is_rgb(x) (!!((x) & 0x10)) |
100 | #define fimc_fmt_is_jpeg(x) (!!((x) & 0x40)) | ||
98 | 101 | ||
99 | #define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ | 102 | #define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ |
100 | __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | 103 | __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) |
@@ -116,9 +119,10 @@ enum fimc_color_fmt { | |||
116 | #define FIMC_DST_ADDR (1 << 2) | 119 | #define FIMC_DST_ADDR (1 << 2) |
117 | #define FIMC_SRC_FMT (1 << 3) | 120 | #define FIMC_SRC_FMT (1 << 3) |
118 | #define FIMC_DST_FMT (1 << 4) | 121 | #define FIMC_DST_FMT (1 << 4) |
119 | #define FIMC_CTX_M2M (1 << 5) | 122 | #define FIMC_DST_CROP (1 << 5) |
120 | #define FIMC_CTX_CAP (1 << 6) | 123 | #define FIMC_CTX_M2M (1 << 16) |
121 | #define FIMC_CTX_SHUT (1 << 7) | 124 | #define FIMC_CTX_CAP (1 << 17) |
125 | #define FIMC_CTX_SHUT (1 << 18) | ||
122 | 126 | ||
123 | /* Image conversion flags */ | 127 | /* Image conversion flags */ |
124 | #define FIMC_IN_DMA_ACCESS_TILED (1 << 0) | 128 | #define FIMC_IN_DMA_ACCESS_TILED (1 << 0) |
@@ -293,12 +297,18 @@ struct fimc_m2m_device { | |||
293 | int refcnt; | 297 | int refcnt; |
294 | }; | 298 | }; |
295 | 299 | ||
300 | #define FIMC_SD_PAD_SINK 0 | ||
301 | #define FIMC_SD_PAD_SOURCE 1 | ||
302 | #define FIMC_SD_PADS_NUM 2 | ||
303 | |||
296 | /** | 304 | /** |
297 | * struct fimc_vid_cap - camera capture device information | 305 | * struct fimc_vid_cap - camera capture device information |
298 | * @ctx: hardware context data | 306 | * @ctx: hardware context data |
299 | * @vfd: video device node for camera capture mode | 307 | * @vfd: video device node for camera capture mode |
308 | * @subdev: subdev exposing the FIMC processing block | ||
300 | * @vd_pad: fimc video capture node pad | 309 | * @vd_pad: fimc video capture node pad |
301 | * @fmt: Media Bus format configured at selected image sensor | 310 | * @sd_pads: fimc video processing block pads |
311 | * @mf: media bus format at the FIMC camera input (and the scaler output) pad | ||
302 | * @pending_buf_q: the pending buffer queue head | 312 | * @pending_buf_q: the pending buffer queue head |
303 | * @active_buf_q: the queue head of buffers scheduled in hardware | 313 | * @active_buf_q: the queue head of buffers scheduled in hardware |
304 | * @vbq: the capture am video buffer queue | 314 | * @vbq: the capture am video buffer queue |
@@ -315,8 +325,10 @@ struct fimc_vid_cap { | |||
315 | struct fimc_ctx *ctx; | 325 | struct fimc_ctx *ctx; |
316 | struct vb2_alloc_ctx *alloc_ctx; | 326 | struct vb2_alloc_ctx *alloc_ctx; |
317 | struct video_device *vfd; | 327 | struct video_device *vfd; |
328 | struct v4l2_subdev *subdev; | ||
318 | struct media_pad vd_pad; | 329 | struct media_pad vd_pad; |
319 | struct v4l2_mbus_framefmt fmt; | 330 | struct v4l2_mbus_framefmt mf; |
331 | struct media_pad sd_pads[FIMC_SD_PADS_NUM]; | ||
320 | struct list_head pending_buf_q; | 332 | struct list_head pending_buf_q; |
321 | struct list_head active_buf_q; | 333 | struct list_head active_buf_q; |
322 | struct vb2_queue vbq; | 334 | struct vb2_queue vbq; |
@@ -498,6 +510,33 @@ struct fimc_ctx { | |||
498 | 510 | ||
499 | #define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) | 511 | #define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) |
500 | 512 | ||
513 | static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height) | ||
514 | { | ||
515 | f->o_width = width; | ||
516 | f->o_height = height; | ||
517 | f->f_width = width; | ||
518 | f->f_height = height; | ||
519 | } | ||
520 | |||
521 | static inline void set_frame_crop(struct fimc_frame *f, | ||
522 | u32 left, u32 top, u32 width, u32 height) | ||
523 | { | ||
524 | f->offs_h = left; | ||
525 | f->offs_v = top; | ||
526 | f->width = width; | ||
527 | f->height = height; | ||
528 | } | ||
529 | |||
530 | static inline u32 fimc_get_format_depth(struct fimc_fmt *ff) | ||
531 | { | ||
532 | u32 i, depth = 0; | ||
533 | |||
534 | if (ff != NULL) | ||
535 | for (i = 0; i < ff->colplanes; i++) | ||
536 | depth += ff->depth[i]; | ||
537 | return depth; | ||
538 | } | ||
539 | |||
501 | static inline bool fimc_capture_active(struct fimc_dev *fimc) | 540 | static inline bool fimc_capture_active(struct fimc_dev *fimc) |
502 | { | 541 | { |
503 | unsigned long flags; | 542 | unsigned long flags; |
@@ -649,7 +688,6 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, | |||
649 | /* fimc-core.c */ | 688 | /* fimc-core.c */ |
650 | int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, | 689 | int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, |
651 | struct v4l2_fmtdesc *f); | 690 | struct v4l2_fmtdesc *f); |
652 | int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr); | ||
653 | int fimc_ctrls_create(struct fimc_ctx *ctx); | 691 | int fimc_ctrls_create(struct fimc_ctx *ctx); |
654 | void fimc_ctrls_delete(struct fimc_ctx *ctx); | 692 | void fimc_ctrls_delete(struct fimc_ctx *ctx); |
655 | void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); | 693 | void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); |
@@ -684,6 +722,7 @@ int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, | |||
684 | struct fimc_vid_buffer *fimc_vb); | 722 | struct fimc_vid_buffer *fimc_vb); |
685 | int fimc_capture_suspend(struct fimc_dev *fimc); | 723 | int fimc_capture_suspend(struct fimc_dev *fimc); |
686 | int fimc_capture_resume(struct fimc_dev *fimc); | 724 | int fimc_capture_resume(struct fimc_dev *fimc); |
725 | int fimc_capture_config_update(struct fimc_ctx *ctx); | ||
687 | 726 | ||
688 | /* Locking: the caller holds fimc->slock */ | 727 | /* Locking: the caller holds fimc->slock */ |
689 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) | 728 | static inline void fimc_activate_capture(struct fimc_ctx *ctx) |
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 5bb2c0de2cdc..db17a6fad3e0 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c | |||
@@ -416,7 +416,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, | |||
416 | struct fimc_sensor_info *s_info; | 416 | struct fimc_sensor_info *s_info; |
417 | struct media_entity *sink; | 417 | struct media_entity *sink; |
418 | unsigned int flags; | 418 | unsigned int flags; |
419 | int ret, i, src_pad; | 419 | int ret, i; |
420 | 420 | ||
421 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | 421 | for (i = 0; i < FIMC_MAX_DEVS; i++) { |
422 | if (!fmd->fimc[i]) | 422 | if (!fmd->fimc[i]) |
@@ -425,20 +425,27 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, | |||
425 | * Some FIMC variants are not fitted with camera capture | 425 | * Some FIMC variants are not fitted with camera capture |
426 | * interface. Skip creating a link from sensor for those. | 426 | * interface. Skip creating a link from sensor for those. |
427 | */ | 427 | */ |
428 | if (sensor && sensor->grp_id == SENSOR_GROUP_ID && | 428 | if (sensor->grp_id == SENSOR_GROUP_ID && |
429 | !fmd->fimc[i]->variant->has_cam_if) | 429 | !fmd->fimc[i]->variant->has_cam_if) |
430 | continue; | 430 | continue; |
431 | 431 | ||
432 | flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; | 432 | flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; |
433 | sink = &fmd->fimc[i]->vid_cap.vfd->entity; | 433 | sink = &fmd->fimc[i]->vid_cap.subdev->entity; |
434 | ret = media_entity_create_link(source, 0, sink, 0, flags); | 434 | ret = media_entity_create_link(source, pad, sink, |
435 | FIMC_SD_PAD_SINK, flags); | ||
435 | if (ret) | 436 | if (ret) |
436 | return ret; | 437 | return ret; |
437 | 438 | ||
439 | /* Notify FIMC capture subdev entity */ | ||
440 | ret = media_entity_call(sink, link_setup, &sink->pads[0], | ||
441 | &source->pads[pad], flags); | ||
442 | if (ret) | ||
443 | break; | ||
444 | |||
438 | v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", | 445 | v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", |
439 | source->name, flags ? '=' : '-', sink->name); | 446 | source->name, flags ? '=' : '-', sink->name); |
440 | 447 | ||
441 | if (flags == 0 || sensor == NULL) | 448 | if (flags == 0) |
442 | continue; | 449 | continue; |
443 | s_info = v4l2_get_subdev_hostdata(sensor); | 450 | s_info = v4l2_get_subdev_hostdata(sensor); |
444 | if (!WARN_ON(s_info == NULL)) { | 451 | if (!WARN_ON(s_info == NULL)) { |
@@ -468,10 +475,10 @@ static int fimc_md_create_links(struct fimc_md *fmd) | |||
468 | struct v4l2_subdev *sensor, *csis; | 475 | struct v4l2_subdev *sensor, *csis; |
469 | struct s5p_fimc_isp_info *pdata; | 476 | struct s5p_fimc_isp_info *pdata; |
470 | struct fimc_sensor_info *s_info; | 477 | struct fimc_sensor_info *s_info; |
471 | struct media_entity *source; | 478 | struct media_entity *source, *sink; |
472 | int fimc_id = 0; | 479 | int i, pad, fimc_id = 0; |
473 | int i, pad; | ||
474 | int ret = 0; | 480 | int ret = 0; |
481 | u32 flags; | ||
475 | 482 | ||
476 | for (i = 0; i < fmd->num_sensors; i++) { | 483 | for (i = 0; i < fmd->num_sensors; i++) { |
477 | if (fmd->sensor[i].subdev == NULL) | 484 | if (fmd->sensor[i].subdev == NULL) |
@@ -507,7 +514,6 @@ static int fimc_md_create_links(struct fimc_md *fmd) | |||
507 | v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", | 514 | v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", |
508 | sensor->entity.name, csis->entity.name); | 515 | sensor->entity.name, csis->entity.name); |
509 | 516 | ||
510 | sensor = NULL; | ||
511 | source = &csis->entity; | 517 | source = &csis->entity; |
512 | pad = CSIS_PAD_SOURCE; | 518 | pad = CSIS_PAD_SOURCE; |
513 | break; | 519 | break; |
@@ -526,8 +532,21 @@ static int fimc_md_create_links(struct fimc_md *fmd) | |||
526 | continue; | 532 | continue; |
527 | 533 | ||
528 | ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, | 534 | ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, |
529 | fimc_id++); | 535 | fimc_id++); |
530 | } | 536 | } |
537 | /* Create immutable links between each FIMC's subdev and video node */ | ||
538 | flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; | ||
539 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | ||
540 | if (!fmd->fimc[i]) | ||
541 | continue; | ||
542 | source = &fmd->fimc[i]->vid_cap.subdev->entity; | ||
543 | sink = &fmd->fimc[i]->vid_cap.vfd->entity; | ||
544 | ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, | ||
545 | sink, 0, flags); | ||
546 | if (ret) | ||
547 | break; | ||
548 | } | ||
549 | |||
531 | return ret; | 550 | return ret; |
532 | } | 551 | } |
533 | 552 | ||
@@ -636,15 +655,15 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) | |||
636 | static int fimc_md_link_notify(struct media_pad *source, | 655 | static int fimc_md_link_notify(struct media_pad *source, |
637 | struct media_pad *sink, u32 flags) | 656 | struct media_pad *sink, u32 flags) |
638 | { | 657 | { |
639 | struct video_device *vid_dev; | 658 | struct v4l2_subdev *sd; |
640 | struct fimc_dev *fimc; | 659 | struct fimc_dev *fimc; |
641 | int ret = 0; | 660 | int ret = 0; |
642 | 661 | ||
643 | if (WARN_ON(media_entity_type(sink->entity) != MEDIA_ENT_T_DEVNODE)) | 662 | if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) |
644 | return 0; | 663 | return 0; |
645 | 664 | ||
646 | vid_dev = media_entity_to_video_device(sink->entity); | 665 | sd = media_entity_to_v4l2_subdev(sink->entity); |
647 | fimc = video_get_drvdata(vid_dev); | 666 | fimc = v4l2_get_subdevdata(sd); |
648 | 667 | ||
649 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | 668 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { |
650 | ret = __fimc_pipeline_shutdown(fimc); | 669 | ret = __fimc_pipeline_shutdown(fimc); |
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 50937b40854f..a1fff022c5b4 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c | |||
@@ -572,7 +572,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, | |||
572 | 572 | ||
573 | if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { | 573 | if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { |
574 | for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { | 574 | for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { |
575 | if (fimc->vid_cap.fmt.code == pix_desc[i].pixelcode) { | 575 | if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { |
576 | cfg = pix_desc[i].cisrcfmt; | 576 | cfg = pix_desc[i].cisrcfmt; |
577 | bus_width = pix_desc[i].bus_width; | 577 | bus_width = pix_desc[i].bus_width; |
578 | break; | 578 | break; |
@@ -582,7 +582,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, | |||
582 | if (i == ARRAY_SIZE(pix_desc)) { | 582 | if (i == ARRAY_SIZE(pix_desc)) { |
583 | v4l2_err(fimc->vid_cap.vfd, | 583 | v4l2_err(fimc->vid_cap.vfd, |
584 | "Camera color format not supported: %d\n", | 584 | "Camera color format not supported: %d\n", |
585 | fimc->vid_cap.fmt.code); | 585 | fimc->vid_cap.mf.code); |
586 | return -EINVAL; | 586 | return -EINVAL; |
587 | } | 587 | } |
588 | 588 | ||
@@ -642,12 +642,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, | |||
642 | cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; | 642 | cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; |
643 | 643 | ||
644 | /* TODO: add remaining supported formats. */ | 644 | /* TODO: add remaining supported formats. */ |
645 | if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) { | 645 | if (vid_cap->mf.code == V4L2_MBUS_FMT_VYUY8_2X8) { |
646 | tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; | 646 | tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; |
647 | } else { | 647 | } else { |
648 | v4l2_err(fimc->vid_cap.vfd, | 648 | v4l2_err(fimc->vid_cap.vfd, |
649 | "Not supported camera pixel format: %d", | 649 | "Not supported camera pixel format: %d", |
650 | vid_cap->fmt.code); | 650 | vid_cap->mf.code); |
651 | return -EINVAL; | 651 | return -EINVAL; |
652 | } | 652 | } |
653 | tmp |= (cam->csi_data_align == 32) << 8; | 653 | tmp |= (cam->csi_data_align == 32) << 8; |