diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2009-08-25 10:46:54 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-18 23:19:04 -0400 |
commit | 961801bbb3448a86f0cc93747cecbfae686d81d1 (patch) | |
tree | 29e7eb9f3779fb645473b9fb32a713bd5fc7fe74 | |
parent | 4a5a5b2d620c3ec362936c4fe3799a29f8de163c (diff) |
V4L/DVB (12528): sh_mobile_ceu_camera: implement host-side image scaling
Use host-side image scaling when the client fails to set the requested format.
We also have to take scaling into account when performing host-side cropping.
Similar to cropping we try to use client-side scaling as much as possible to
preserve bus bandwidth and optimise the frame-rate.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 392 |
1 files changed, 321 insertions, 71 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 3fee7c01fd57..499d1a235fd7 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -95,6 +95,8 @@ struct sh_mobile_ceu_dev { | |||
95 | 95 | ||
96 | struct sh_mobile_ceu_info *pdata; | 96 | struct sh_mobile_ceu_info *pdata; |
97 | 97 | ||
98 | u32 cflcr; | ||
99 | |||
98 | unsigned int is_interlaced:1; | 100 | unsigned int is_interlaced:1; |
99 | unsigned int image_mode:1; | 101 | unsigned int image_mode:1; |
100 | unsigned int is_16bit:1; | 102 | unsigned int is_16bit:1; |
@@ -102,6 +104,7 @@ struct sh_mobile_ceu_dev { | |||
102 | 104 | ||
103 | struct sh_mobile_ceu_cam { | 105 | struct sh_mobile_ceu_cam { |
104 | struct v4l2_rect camera_rect; | 106 | struct v4l2_rect camera_rect; |
107 | struct v4l2_rect camera_max; | ||
105 | const struct soc_camera_data_format *extra_fmt; | 108 | const struct soc_camera_data_format *extra_fmt; |
106 | const struct soc_camera_data_format *camera_fmt; | 109 | const struct soc_camera_data_format *camera_fmt; |
107 | }; | 110 | }; |
@@ -435,54 +438,119 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | |||
435 | pcdev->icd = NULL; | 438 | pcdev->icd = NULL; |
436 | } | 439 | } |
437 | 440 | ||
441 | /* | ||
442 | * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" | ||
443 | * in SH7722 Hardware Manual | ||
444 | */ | ||
445 | static unsigned int size_dst(unsigned int src, unsigned int scale) | ||
446 | { | ||
447 | unsigned int mant_pre = scale >> 12; | ||
448 | if (!src || !scale) | ||
449 | return src; | ||
450 | return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * | ||
451 | mant_pre * 4096 / scale + 1; | ||
452 | } | ||
453 | |||
454 | static unsigned int size_src(unsigned int dst, unsigned int scale) | ||
455 | { | ||
456 | unsigned int mant_pre = scale >> 12, tmp; | ||
457 | if (!dst || !scale) | ||
458 | return dst; | ||
459 | for (tmp = ((dst - 1) * scale + 2048 * mant_pre) / 4096 + 1; | ||
460 | size_dst(tmp, scale) < dst; | ||
461 | tmp++) | ||
462 | ; | ||
463 | return tmp; | ||
464 | } | ||
465 | |||
466 | static u16 calc_scale(unsigned int src, unsigned int *dst) | ||
467 | { | ||
468 | u16 scale; | ||
469 | |||
470 | if (src == *dst) | ||
471 | return 0; | ||
472 | |||
473 | scale = (src * 4096 / *dst) & ~7; | ||
474 | |||
475 | while (scale > 4096 && size_dst(src, scale) < *dst) | ||
476 | scale -= 8; | ||
477 | |||
478 | *dst = size_dst(src, scale); | ||
479 | |||
480 | return scale; | ||
481 | } | ||
482 | |||
483 | /* rect is guaranteed to not exceed the scaled camera rectangle */ | ||
438 | static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, | 484 | static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, |
439 | struct v4l2_rect *rect) | 485 | struct v4l2_rect *rect) |
440 | { | 486 | { |
441 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 487 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
442 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | 488 | struct sh_mobile_ceu_cam *cam = icd->host_priv; |
443 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 489 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
444 | int width, height, cfszr_width, cdwdr_width; | 490 | int width, height, cfszr_width, cdwdr_width, in_width, in_height; |
445 | unsigned int left_offset, top_offset; | 491 | unsigned int left_offset, top_offset, left, top; |
492 | unsigned int hscale = pcdev->cflcr & 0xffff; | ||
493 | unsigned int vscale = (pcdev->cflcr >> 16) & 0xffff; | ||
446 | u32 camor; | 494 | u32 camor; |
447 | 495 | ||
448 | if (rect->left > cam->camera_rect.left) { | 496 | /* Switch to the camera scale */ |
449 | left_offset = rect->left - cam->camera_rect.left; | 497 | left = size_src(rect->left, hscale); |
498 | top = size_src(rect->top, vscale); | ||
499 | |||
500 | dev_dbg(&icd->dev, "Left %u * 0x%x = %u, top %u * 0x%x = %u\n", | ||
501 | rect->left, hscale, left, rect->top, vscale, top); | ||
502 | |||
503 | if (left > cam->camera_rect.left) { | ||
504 | left_offset = left - cam->camera_rect.left; | ||
450 | } else { | 505 | } else { |
451 | left_offset = 0; | 506 | left_offset = 0; |
452 | rect->left = cam->camera_rect.left; | 507 | left = cam->camera_rect.left; |
453 | } | 508 | } |
454 | 509 | ||
455 | if (rect->top > cam->camera_rect.top) { | 510 | if (top > cam->camera_rect.top) { |
456 | top_offset = rect->top - cam->camera_rect.top; | 511 | top_offset = top - cam->camera_rect.top; |
457 | } else { | 512 | } else { |
458 | top_offset = 0; | 513 | top_offset = 0; |
459 | rect->top = cam->camera_rect.top; | 514 | top = cam->camera_rect.top; |
460 | } | 515 | } |
461 | 516 | ||
462 | dev_dbg(&icd->dev, "Offsets %u:%u\n", left_offset, top_offset); | 517 | dev_dbg(&icd->dev, "New left %u, top %u, offsets %u:%u\n", |
518 | rect->left, rect->top, left_offset, top_offset); | ||
463 | 519 | ||
464 | if (pcdev->image_mode) { | 520 | if (pcdev->image_mode) { |
465 | width = rect->width; | 521 | width = rect->width; |
466 | if (!pcdev->is_16bit) | 522 | in_width = cam->camera_rect.width; |
523 | if (!pcdev->is_16bit) { | ||
467 | width *= 2; | 524 | width *= 2; |
525 | in_width *= 2; | ||
526 | left_offset *= 2; | ||
527 | } | ||
468 | cfszr_width = cdwdr_width = rect->width; | 528 | cfszr_width = cdwdr_width = rect->width; |
469 | } else { | 529 | } else { |
470 | width = rect->width * | 530 | unsigned int w_factor = (icd->current_fmt->depth + 7) >> 3; |
471 | ((icd->current_fmt->depth + 7) >> 3); | 531 | if (!pcdev->is_16bit) |
472 | width = pcdev->is_16bit ? width / 2 : width; | 532 | w_factor *= 2; |
533 | |||
534 | width = rect->width * w_factor / 2; | ||
535 | in_width = cam->camera_rect.width * w_factor / 2; | ||
536 | left_offset = left_offset * w_factor / 2; | ||
537 | |||
473 | cfszr_width = pcdev->is_16bit ? width : width / 2; | 538 | cfszr_width = pcdev->is_16bit ? width : width / 2; |
474 | cdwdr_width = pcdev->is_16bit ? width * 2 : width; | 539 | cdwdr_width = pcdev->is_16bit ? width * 2 : width; |
475 | } | 540 | } |
476 | 541 | ||
477 | height = rect->height; | 542 | height = rect->height; |
543 | in_height = cam->camera_rect.height; | ||
478 | if (pcdev->is_interlaced) { | 544 | if (pcdev->is_interlaced) { |
479 | height /= 2; | 545 | height /= 2; |
546 | in_height /= 2; | ||
547 | top_offset /= 2; | ||
480 | cdwdr_width *= 2; | 548 | cdwdr_width *= 2; |
481 | } | 549 | } |
482 | 550 | ||
483 | camor = left_offset | (top_offset << 16); | 551 | camor = left_offset | (top_offset << 16); |
484 | ceu_write(pcdev, CAMOR, camor); | 552 | ceu_write(pcdev, CAMOR, camor); |
485 | ceu_write(pcdev, CAPWR, (height << 16) | width); | 553 | ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); |
486 | ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); | 554 | ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); |
487 | ceu_write(pcdev, CDWDR, cdwdr_width); | 555 | ceu_write(pcdev, CDWDR, cdwdr_width); |
488 | } | 556 | } |
@@ -556,7 +624,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
556 | ceu_write(pcdev, CRCMPR, 0); | 624 | ceu_write(pcdev, CRCMPR, 0); |
557 | 625 | ||
558 | value = 0x00000010; /* data fetch by default */ | 626 | value = 0x00000010; /* data fetch by default */ |
559 | pcdev->image_mode = yuv_lineskip = 0; | 627 | yuv_lineskip = 0; |
560 | 628 | ||
561 | switch (icd->current_fmt->fourcc) { | 629 | switch (icd->current_fmt->fourcc) { |
562 | case V4L2_PIX_FMT_NV12: | 630 | case V4L2_PIX_FMT_NV12: |
@@ -565,7 +633,6 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
565 | /* fall-through */ | 633 | /* fall-through */ |
566 | case V4L2_PIX_FMT_NV16: | 634 | case V4L2_PIX_FMT_NV16: |
567 | case V4L2_PIX_FMT_NV61: | 635 | case V4L2_PIX_FMT_NV61: |
568 | pcdev->image_mode = 1; | ||
569 | switch (cam->camera_fmt->fourcc) { | 636 | switch (cam->camera_fmt->fourcc) { |
570 | case V4L2_PIX_FMT_UYVY: | 637 | case V4L2_PIX_FMT_UYVY: |
571 | value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ | 638 | value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ |
@@ -599,7 +666,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
599 | mdelay(1); | 666 | mdelay(1); |
600 | sh_mobile_ceu_set_rect(icd, &icd->rect_current); | 667 | sh_mobile_ceu_set_rect(icd, &icd->rect_current); |
601 | 668 | ||
602 | ceu_write(pcdev, CFLCR, 0); /* no scaling */ | 669 | ceu_write(pcdev, CFLCR, pcdev->cflcr); |
603 | 670 | ||
604 | /* A few words about byte order (observed in Big Endian mode) | 671 | /* A few words about byte order (observed in Big Endian mode) |
605 | * | 672 | * |
@@ -620,7 +687,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, | |||
620 | ceu_write(pcdev, CDOCR, value); | 687 | ceu_write(pcdev, CDOCR, value); |
621 | ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ | 688 | ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ |
622 | 689 | ||
623 | dev_dbg(&icd->dev, "S_FMT successful for %c%c%c%c %ux%u@%u.%u\n", | 690 | dev_dbg(&icd->dev, "S_FMT successful for %c%c%c%c %ux%u@%u:%u\n", |
624 | pixfmt & 0xff, (pixfmt >> 8) & 0xff, | 691 | pixfmt & 0xff, (pixfmt >> 8) & 0xff, |
625 | (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, | 692 | (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, |
626 | icd->rect_current.width, icd->rect_current.height, | 693 | icd->rect_current.width, icd->rect_current.height, |
@@ -692,6 +759,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, | |||
692 | return -ENOMEM; | 759 | return -ENOMEM; |
693 | 760 | ||
694 | icd->host_priv = cam; | 761 | icd->host_priv = cam; |
762 | cam->camera_max = icd->rect_max; | ||
695 | } else { | 763 | } else { |
696 | cam = icd->host_priv; | 764 | cam = icd->host_priv; |
697 | } | 765 | } |
@@ -754,86 +822,150 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) | |||
754 | icd->host_priv = NULL; | 822 | icd->host_priv = NULL; |
755 | } | 823 | } |
756 | 824 | ||
825 | /* Check if any dimension of r1 is smaller than respective one of r2 */ | ||
757 | static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) | 826 | static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) |
758 | { | 827 | { |
759 | return r1->width < r2->width || r1->height < r2->height; | 828 | return r1->width < r2->width || r1->height < r2->height; |
760 | } | 829 | } |
761 | 830 | ||
831 | /* Check if r1 fails to cover r2 */ | ||
832 | static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2) | ||
833 | { | ||
834 | return r1->left > r2->left || r1->top > r2->top || | ||
835 | r1->left + r1->width < r2->left + r2->width || | ||
836 | r1->top + r1->height < r2->top + r2->height; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * CEU can scale and crop, but we don't want to waste bandwidth and kill the | ||
841 | * framerate by always requesting the maximum image from the client. For | ||
842 | * cropping we also have to take care of the current scale. The common for both | ||
843 | * scaling and cropping approach is: | ||
844 | * 1. try if the client can produce exactly what requested by the user | ||
845 | * 2. if (1) failed, try to double the client image until we get one big enough | ||
846 | * 3. if (2) failed, try to request the maximum image | ||
847 | */ | ||
762 | static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | 848 | static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, |
763 | struct v4l2_rect *rect) | 849 | struct v4l2_rect *rect) |
764 | { | 850 | { |
765 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 851 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
766 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 852 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
767 | struct v4l2_rect cam_rect = *rect; | 853 | struct v4l2_rect cam_rect, target, cam_max; |
768 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | 854 | struct sh_mobile_ceu_cam *cam = icd->host_priv; |
855 | unsigned int hscale = pcdev->cflcr & 0xffff; | ||
856 | unsigned int vscale = (pcdev->cflcr >> 16) & 0xffff; | ||
769 | unsigned short width, height; | 857 | unsigned short width, height; |
770 | u32 capsr; | 858 | u32 capsr; |
771 | int ret; | 859 | int ret; |
772 | 860 | ||
861 | /* Scale back up into client units */ | ||
862 | cam_rect.left = size_src(rect->left, hscale); | ||
863 | cam_rect.width = size_src(rect->width, hscale); | ||
864 | cam_rect.top = size_src(rect->top, vscale); | ||
865 | cam_rect.height = size_src(rect->height, vscale); | ||
866 | |||
867 | target = cam_rect; | ||
868 | |||
773 | capsr = capture_save_reset(pcdev); | 869 | capsr = capture_save_reset(pcdev); |
774 | dev_dbg(&icd->dev, "CAPSR %x\n", capsr); | 870 | dev_dbg(&icd->dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); |
775 | 871 | ||
872 | /* First attempt - see if the client can deliver a perfect result */ | ||
776 | ret = icd->ops->set_crop(icd, &cam_rect); | 873 | ret = icd->ops->set_crop(icd, &cam_rect); |
777 | if (!ret && !memcmp(rect, &cam_rect, sizeof(*rect))) { | 874 | if (!ret && !memcmp(&target, &cam_rect, sizeof(target))) { |
778 | dev_dbg(&icd->dev, "Camera S_CROP successful for %ux%u@%u.%u\n", | 875 | dev_dbg(&icd->dev, "Camera S_CROP successful for %ux%u@%u:%u\n", |
779 | cam_rect.width, cam_rect.height, | 876 | cam_rect.width, cam_rect.height, |
780 | cam_rect.left, cam_rect.top); | 877 | cam_rect.left, cam_rect.top); |
781 | goto ceu_set_rect; | 878 | goto ceu_set_rect; |
782 | } | 879 | } |
783 | 880 | ||
784 | /* Try to fix cropping, that camera hasn't managed to do */ | 881 | /* Try to fix cropping, that camera hasn't managed to do */ |
785 | dev_dbg(&icd->dev, "Fix camera S_CROP %d for %ux%u@%u.%u\n", | 882 | dev_dbg(&icd->dev, "Fix camera S_CROP %d for %ux%u@%u:%u" |
883 | " to %ux%u@%u:%u\n", | ||
786 | ret, cam_rect.width, cam_rect.height, | 884 | ret, cam_rect.width, cam_rect.height, |
787 | cam_rect.left, cam_rect.top); | 885 | cam_rect.left, cam_rect.top, |
886 | target.width, target.height, target.left, target.top); | ||
788 | 887 | ||
789 | /* | 888 | /* |
790 | * Popular special case - some cameras can only handle fixed sizes like | 889 | * Popular special case - some cameras can only handle fixed sizes like |
791 | * QVGA, VGA,... Take care to avoid infinite loop. | 890 | * QVGA, VGA,... Take care to avoid infinite loop. |
792 | */ | 891 | */ |
793 | width = max(cam_rect.width, 1) * 2; | 892 | width = max(cam_rect.width, 1); |
794 | height = max(cam_rect.height, 1) * 2; | 893 | height = max(cam_rect.height, 1); |
795 | while (!ret && is_smaller(&cam_rect, rect) && | 894 | cam_max.width = size_src(icd->rect_max.width, hscale); |
796 | (icd->rect_max.width >= width && | 895 | cam_max.left = size_src(icd->rect_max.left, hscale); |
797 | icd->rect_max.height >= height)) { | 896 | cam_max.height = size_src(icd->rect_max.height, vscale); |
897 | cam_max.top = size_src(icd->rect_max.top, vscale); | ||
898 | while (!ret && (is_smaller(&cam_rect, &target) || | ||
899 | is_inside(&cam_rect, &target)) && | ||
900 | cam_max.width >= width && cam_max.height >= height) { | ||
901 | |||
902 | width *= 2; | ||
903 | height *= 2; | ||
798 | cam_rect.width = width; | 904 | cam_rect.width = width; |
799 | cam_rect.height = height; | 905 | cam_rect.height = height; |
800 | 906 | ||
907 | /* We do not know what the camera is capable of, play safe */ | ||
908 | if (cam_rect.left > target.left) | ||
909 | cam_rect.left = cam_max.left; | ||
910 | |||
911 | if (cam_rect.left + cam_rect.width < target.left + target.width) | ||
912 | cam_rect.width = target.left + target.width - | ||
913 | cam_rect.left; | ||
914 | |||
915 | if (cam_rect.top > target.top) | ||
916 | cam_rect.top = cam_max.top; | ||
917 | |||
918 | if (cam_rect.top + cam_rect.height < target.top + target.height) | ||
919 | cam_rect.height = target.top + target.height - | ||
920 | cam_rect.top; | ||
921 | |||
801 | if (cam_rect.width + cam_rect.left > | 922 | if (cam_rect.width + cam_rect.left > |
802 | icd->rect_max.width + icd->rect_max.left) | 923 | cam_max.width + cam_max.left) |
803 | cam_rect.left = icd->rect_max.width + | 924 | cam_rect.left = max(cam_max.width + cam_max.left - |
804 | icd->rect_max.left - cam_rect.width; | 925 | cam_rect.width, cam_max.left); |
805 | 926 | ||
806 | if (cam_rect.height + cam_rect.top > | 927 | if (cam_rect.height + cam_rect.top > |
807 | icd->rect_max.height + icd->rect_max.top) | 928 | cam_max.height + cam_max.top) |
808 | cam_rect.top = icd->rect_max.height + | 929 | cam_rect.top = max(cam_max.height + cam_max.top - |
809 | icd->rect_max.top - cam_rect.height; | 930 | cam_rect.height, cam_max.top); |
810 | 931 | ||
811 | ret = icd->ops->set_crop(icd, &cam_rect); | 932 | ret = icd->ops->set_crop(icd, &cam_rect); |
812 | dev_dbg(&icd->dev, "Camera S_CROP %d for %ux%u@%u.%u\n", | 933 | dev_dbg(&icd->dev, "Camera S_CROP %d for %ux%u@%u:%u\n", |
813 | ret, cam_rect.width, cam_rect.height, | 934 | ret, cam_rect.width, cam_rect.height, |
814 | cam_rect.left, cam_rect.top); | 935 | cam_rect.left, cam_rect.top); |
815 | width *= 2; | ||
816 | height *= 2; | ||
817 | } | 936 | } |
818 | 937 | ||
819 | /* | 938 | /* |
820 | * If the camera failed to configure cropping, it should not modify the | 939 | * If the camera failed to configure cropping, it should not modify the |
821 | * rectangle | 940 | * rectangle |
822 | */ | 941 | */ |
823 | if ((ret < 0 && is_smaller(&icd->rect_current, rect)) || | 942 | if ((ret < 0 && (is_smaller(&icd->rect_current, rect) || |
824 | is_smaller(&cam_rect, rect)) { | 943 | is_inside(&icd->rect_current, rect))) || |
944 | is_smaller(&cam_rect, &target) || is_inside(&cam_rect, &target)) { | ||
825 | /* | 945 | /* |
826 | * The camera failed to configure a suitable cropping, | 946 | * The camera failed to configure a suitable cropping, |
827 | * we cannot use the current rectangle, set to max | 947 | * we cannot use the current rectangle, set to max |
828 | */ | 948 | */ |
829 | cam_rect = icd->rect_max; | 949 | cam_rect = cam_max; |
830 | ret = icd->ops->set_crop(icd, &cam_rect); | 950 | ret = icd->ops->set_crop(icd, &cam_rect); |
831 | dev_dbg(&icd->dev, "Camera S_CROP %d for max %ux%u@%u.%u\n", | 951 | dev_dbg(&icd->dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", |
832 | ret, cam_rect.width, cam_rect.height, | 952 | ret, cam_rect.width, cam_rect.height, |
833 | cam_rect.left, cam_rect.top); | 953 | cam_rect.left, cam_rect.top); |
834 | if (ret < 0) | 954 | if (ret < 0) |
835 | /* All failed, hopefully resume current capture */ | 955 | /* All failed, hopefully resume current capture */ |
836 | goto resume_capture; | 956 | goto resume_capture; |
957 | |||
958 | /* Finally, adjust the target rectangle */ | ||
959 | if (target.width > cam_rect.width) | ||
960 | target.width = cam_rect.width; | ||
961 | if (target.height > cam_rect.height) | ||
962 | target.height = cam_rect.height; | ||
963 | if (target.left + target.width > cam_rect.left + cam_rect.width) | ||
964 | target.left = cam_rect.left + cam_rect.width - | ||
965 | target.width; | ||
966 | if (target.top + target.height > cam_rect.top + cam_rect.height) | ||
967 | target.top = cam_rect.top + cam_rect.height - | ||
968 | target.height; | ||
837 | } | 969 | } |
838 | 970 | ||
839 | /* We now have a rectangle, larger than requested, let's crop */ | 971 | /* We now have a rectangle, larger than requested, let's crop */ |
@@ -844,8 +976,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
844 | * last before last close() _user_ rectangle, which can be different | 976 | * last before last close() _user_ rectangle, which can be different |
845 | * from camera rectangle. | 977 | * from camera rectangle. |
846 | */ | 978 | */ |
847 | dev_dbg(&icd->dev, "SH S_CROP from %ux%u@%u.%u to %ux%u@%u.%u\n", | 979 | dev_dbg(&icd->dev, |
980 | "SH S_CROP from %ux%u@%u:%u to %ux%u@%u:%u, scale to %ux%u@%u:%u\n", | ||
848 | cam_rect.width, cam_rect.height, cam_rect.left, cam_rect.top, | 981 | cam_rect.width, cam_rect.height, cam_rect.left, cam_rect.top, |
982 | target.width, target.height, target.left, target.top, | ||
849 | rect->width, rect->height, rect->left, rect->top); | 983 | rect->width, rect->height, rect->left, rect->top); |
850 | 984 | ||
851 | ret = 0; | 985 | ret = 0; |
@@ -853,27 +987,49 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
853 | ceu_set_rect: | 987 | ceu_set_rect: |
854 | cam->camera_rect = cam_rect; | 988 | cam->camera_rect = cam_rect; |
855 | 989 | ||
990 | rect->width = size_dst(target.width, hscale); | ||
991 | rect->left = size_dst(target.left, hscale); | ||
992 | rect->height = size_dst(target.height, vscale); | ||
993 | rect->top = size_dst(target.top, vscale); | ||
994 | |||
995 | sh_mobile_ceu_set_rect(icd, rect); | ||
996 | |||
997 | resume_capture: | ||
856 | /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ | 998 | /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ |
857 | if (pcdev->active) | 999 | if (pcdev->active) |
858 | capsr |= 1; | 1000 | capsr |= 1; |
859 | sh_mobile_ceu_set_rect(icd, rect); | ||
860 | capture_restore(pcdev, capsr); | 1001 | capture_restore(pcdev, capsr); |
861 | 1002 | ||
862 | resume_capture: | ||
863 | |||
864 | /* Even if only camera cropping succeeded */ | 1003 | /* Even if only camera cropping succeeded */ |
865 | return ret; | 1004 | return ret; |
866 | } | 1005 | } |
867 | 1006 | ||
1007 | /* Similar to set_crop multistage iterative algorithm */ | ||
868 | static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, | 1008 | static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, |
869 | struct v4l2_format *f) | 1009 | struct v4l2_format *f) |
870 | { | 1010 | { |
871 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 1011 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
1012 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
872 | struct sh_mobile_ceu_cam *cam = icd->host_priv; | 1013 | struct sh_mobile_ceu_cam *cam = icd->host_priv; |
873 | struct v4l2_pix_format *pix = &f->fmt.pix; | 1014 | struct v4l2_pix_format *pix = &f->fmt.pix; |
874 | __u32 pixfmt = pix->pixelformat; | 1015 | __u32 pixfmt = pix->pixelformat; |
875 | const struct soc_camera_format_xlate *xlate; | 1016 | const struct soc_camera_format_xlate *xlate; |
876 | int ret; | 1017 | unsigned int width = pix->width, height = pix->height, tmp_w, tmp_h; |
1018 | u16 vscale, hscale; | ||
1019 | int ret, is_interlaced; | ||
1020 | |||
1021 | switch (pix->field) { | ||
1022 | case V4L2_FIELD_INTERLACED: | ||
1023 | is_interlaced = 1; | ||
1024 | break; | ||
1025 | case V4L2_FIELD_ANY: | ||
1026 | default: | ||
1027 | pix->field = V4L2_FIELD_NONE; | ||
1028 | /* fall-through */ | ||
1029 | case V4L2_FIELD_NONE: | ||
1030 | is_interlaced = 0; | ||
1031 | break; | ||
1032 | } | ||
877 | 1033 | ||
878 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | 1034 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
879 | if (!xlate) { | 1035 | if (!xlate) { |
@@ -884,27 +1040,104 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, | |||
884 | pix->pixelformat = xlate->cam_fmt->fourcc; | 1040 | pix->pixelformat = xlate->cam_fmt->fourcc; |
885 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, s_fmt, f); | 1041 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, s_fmt, f); |
886 | pix->pixelformat = pixfmt; | 1042 | pix->pixelformat = pixfmt; |
887 | if (!ret) { | 1043 | dev_dbg(&icd->dev, "Camera %d fmt %ux%u, requested %ux%u, max %ux%u\n", |
888 | icd->buswidth = xlate->buswidth; | 1044 | ret, pix->width, pix->height, width, height, |
889 | icd->current_fmt = xlate->host_fmt; | 1045 | icd->rect_max.width, icd->rect_max.height); |
890 | cam->camera_fmt = xlate->cam_fmt; | 1046 | if (ret < 0) |
891 | cam->camera_rect.width = pix->width; | 1047 | return ret; |
892 | cam->camera_rect.height = pix->height; | 1048 | |
893 | cam->camera_rect.left = icd->rect_current.left; | 1049 | switch (pixfmt) { |
894 | cam->camera_rect.top = icd->rect_current.top; | 1050 | case V4L2_PIX_FMT_NV12: |
1051 | case V4L2_PIX_FMT_NV21: | ||
1052 | case V4L2_PIX_FMT_NV16: | ||
1053 | case V4L2_PIX_FMT_NV61: | ||
1054 | pcdev->image_mode = 1; | ||
1055 | break; | ||
1056 | default: | ||
1057 | pcdev->image_mode = 0; | ||
895 | } | 1058 | } |
896 | 1059 | ||
897 | return ret; | 1060 | if ((abs(width - pix->width) < 4 && abs(height - pix->height) < 4) || |
1061 | !pcdev->image_mode || is_interlaced) { | ||
1062 | hscale = 0; | ||
1063 | vscale = 0; | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
1067 | /* Camera set a format, but geometry is not precise, try to improve */ | ||
1068 | /* | ||
1069 | * FIXME: when soc-camera is converted to implement traditional S_FMT | ||
1070 | * and S_CROP semantics, replace CEU limits with camera maxima | ||
1071 | */ | ||
1072 | tmp_w = pix->width; | ||
1073 | tmp_h = pix->height; | ||
1074 | while ((width > tmp_w || height > tmp_h) && | ||
1075 | tmp_w < 2560 && tmp_h < 1920) { | ||
1076 | tmp_w = min(2 * tmp_w, (__u32)2560); | ||
1077 | tmp_h = min(2 * tmp_h, (__u32)1920); | ||
1078 | pix->width = tmp_w; | ||
1079 | pix->height = tmp_h; | ||
1080 | pix->pixelformat = xlate->cam_fmt->fourcc; | ||
1081 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, | ||
1082 | video, s_fmt, f); | ||
1083 | pix->pixelformat = pixfmt; | ||
1084 | dev_dbg(&icd->dev, "Camera scaled to %ux%u\n", | ||
1085 | pix->width, pix->height); | ||
1086 | if (ret < 0) { | ||
1087 | /* This shouldn't happen */ | ||
1088 | dev_err(&icd->dev, "Client failed to set format: %d\n", | ||
1089 | ret); | ||
1090 | return ret; | ||
1091 | } | ||
1092 | } | ||
1093 | |||
1094 | /* We cannot scale up */ | ||
1095 | if (width > pix->width) | ||
1096 | width = pix->width; | ||
1097 | |||
1098 | if (height > pix->height) | ||
1099 | height = pix->height; | ||
1100 | |||
1101 | /* Let's rock: scale pix->{width x height} down to width x height */ | ||
1102 | hscale = calc_scale(pix->width, &width); | ||
1103 | vscale = calc_scale(pix->height, &height); | ||
1104 | |||
1105 | dev_dbg(&icd->dev, "W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", | ||
1106 | pix->width, hscale, width, pix->height, vscale, height); | ||
1107 | |||
1108 | out: | ||
1109 | pcdev->cflcr = hscale | (vscale << 16); | ||
1110 | |||
1111 | icd->buswidth = xlate->buswidth; | ||
1112 | icd->current_fmt = xlate->host_fmt; | ||
1113 | cam->camera_fmt = xlate->cam_fmt; | ||
1114 | cam->camera_rect.width = pix->width; | ||
1115 | cam->camera_rect.height = pix->height; | ||
1116 | |||
1117 | icd->rect_max.left = size_dst(cam->camera_max.left, hscale); | ||
1118 | icd->rect_max.width = size_dst(cam->camera_max.width, hscale); | ||
1119 | icd->rect_max.top = size_dst(cam->camera_max.top, vscale); | ||
1120 | icd->rect_max.height = size_dst(cam->camera_max.height, vscale); | ||
1121 | |||
1122 | icd->rect_current.left = icd->rect_max.left; | ||
1123 | icd->rect_current.top = icd->rect_max.top; | ||
1124 | |||
1125 | pcdev->is_interlaced = is_interlaced; | ||
1126 | |||
1127 | pix->width = width; | ||
1128 | pix->height = height; | ||
1129 | |||
1130 | return 0; | ||
898 | } | 1131 | } |
899 | 1132 | ||
900 | static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | 1133 | static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, |
901 | struct v4l2_format *f) | 1134 | struct v4l2_format *f) |
902 | { | 1135 | { |
903 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 1136 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
904 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
905 | const struct soc_camera_format_xlate *xlate; | 1137 | const struct soc_camera_format_xlate *xlate; |
906 | struct v4l2_pix_format *pix = &f->fmt.pix; | 1138 | struct v4l2_pix_format *pix = &f->fmt.pix; |
907 | __u32 pixfmt = pix->pixelformat; | 1139 | __u32 pixfmt = pix->pixelformat; |
1140 | int width, height; | ||
908 | int ret; | 1141 | int ret; |
909 | 1142 | ||
910 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | 1143 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
@@ -918,6 +1151,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
918 | v4l_bound_align_image(&pix->width, 2, 2560, 1, | 1151 | v4l_bound_align_image(&pix->width, 2, 2560, 1, |
919 | &pix->height, 4, 1920, 2, 0); | 1152 | &pix->height, 4, 1920, 2, 0); |
920 | 1153 | ||
1154 | width = pix->width; | ||
1155 | height = pix->height; | ||
1156 | |||
921 | pix->bytesperline = pix->width * | 1157 | pix->bytesperline = pix->width * |
922 | DIV_ROUND_UP(xlate->host_fmt->depth, 8); | 1158 | DIV_ROUND_UP(xlate->host_fmt->depth, 8); |
923 | pix->sizeimage = pix->height * pix->bytesperline; | 1159 | pix->sizeimage = pix->height * pix->bytesperline; |
@@ -925,24 +1161,38 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
925 | pix->pixelformat = xlate->cam_fmt->fourcc; | 1161 | pix->pixelformat = xlate->cam_fmt->fourcc; |
926 | 1162 | ||
927 | /* limit to sensor capabilities */ | 1163 | /* limit to sensor capabilities */ |
928 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, try_fmt, f); | 1164 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, (__u32)icd, video, |
1165 | try_fmt, f); | ||
929 | pix->pixelformat = pixfmt; | 1166 | pix->pixelformat = pixfmt; |
930 | if (ret < 0) | 1167 | if (ret < 0) |
931 | return ret; | 1168 | return ret; |
932 | 1169 | ||
933 | switch (pix->field) { | 1170 | switch (pixfmt) { |
934 | case V4L2_FIELD_INTERLACED: | 1171 | case V4L2_PIX_FMT_NV12: |
935 | pcdev->is_interlaced = 1; | 1172 | case V4L2_PIX_FMT_NV21: |
936 | break; | 1173 | case V4L2_PIX_FMT_NV16: |
937 | case V4L2_FIELD_ANY: | 1174 | case V4L2_PIX_FMT_NV61: |
938 | pix->field = V4L2_FIELD_NONE; | 1175 | /* FIXME: check against rect_max after converting soc-camera */ |
939 | /* fall-through */ | 1176 | /* We can scale precisely, need a bigger image from camera */ |
940 | case V4L2_FIELD_NONE: | 1177 | if (pix->width < width || pix->height < height) { |
941 | pcdev->is_interlaced = 0; | 1178 | int tmp_w = pix->width, tmp_h = pix->height; |
942 | break; | 1179 | pix->width = 2560; |
943 | default: | 1180 | pix->height = 1920; |
944 | ret = -EINVAL; | 1181 | ret = v4l2_device_call_until_err(&ici->v4l2_dev, |
945 | break; | 1182 | (__u32)icd, video, |
1183 | try_fmt, f); | ||
1184 | if (ret < 0) { | ||
1185 | /* Shouldn't actually happen... */ | ||
1186 | dev_err(&icd->dev, | ||
1187 | "FIXME: try_fmt() returned %d\n", ret); | ||
1188 | pix->width = tmp_w; | ||
1189 | pix->height = tmp_h; | ||
1190 | } | ||
1191 | } | ||
1192 | if (pix->width > width) | ||
1193 | pix->width = width; | ||
1194 | if (pix->height > height) | ||
1195 | pix->height = height; | ||
946 | } | 1196 | } |
947 | 1197 | ||
948 | return ret; | 1198 | return ret; |