diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2014-05-19 18:40:04 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-08-21 16:25:14 -0400 |
commit | bcb4e0efd1380d93866df51ec5d8dfaa026537ad (patch) | |
tree | dc52f14b55b69cea7d8eb283edd9e48d2e9df768 | |
parent | 9a36d8ed33c481a99f69f8a2eeb22e3c7750e522 (diff) |
[media] omap3isp: ccdc: Support the interlaced field orders at the CCDC output
The CCDC can interleave fields into a single buffer when writing to
memory. Support it.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Enrico Butera <ebutera@users.sourceforge.net>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/platform/omap3isp/ispccdc.c | 132 | ||||
-rw-r--r-- | drivers/media/platform/omap3isp/ispreg.h | 10 | ||||
-rw-r--r-- | drivers/media/platform/omap3isp/ispvideo.c | 40 |
3 files changed, 122 insertions, 60 deletions
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 49d7256a7de3..8d1861d948d0 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c | |||
@@ -863,52 +863,51 @@ static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) | |||
863 | /* | 863 | /* |
864 | * ccdc_config_outlineoffset - Configure memory saving output line offset | 864 | * ccdc_config_outlineoffset - Configure memory saving output line offset |
865 | * @ccdc: Pointer to ISP CCDC device. | 865 | * @ccdc: Pointer to ISP CCDC device. |
866 | * @offset: Address offset to start a new line. Must be twice the | 866 | * @bpl: Number of bytes per line when stored in memory. |
867 | * Output width and aligned on 32 byte boundary | 867 | * @field: Field order when storing interlaced formats in memory. |
868 | * @oddeven: Specifies the odd/even line pattern to be chosen to store the | ||
869 | * output. | ||
870 | * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. | ||
871 | * | 868 | * |
872 | * - Configures the output line offset when stored in memory | 869 | * Configure the offsets for the line output control: |
873 | * - Sets the odd/even line pattern to store the output | 870 | * |
874 | * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) | 871 | * - The horizontal line offset is defined as the number of bytes between the |
875 | * - Configures the number of even and odd line fields in case of rearranging | 872 | * start of two consecutive lines in memory. Set it to the given bytes per |
876 | * the lines. | 873 | * line value. |
874 | * | ||
875 | * - The field offset value is defined as the number of lines to offset the | ||
876 | * start of the field identified by FID = 1. Set it to one. | ||
877 | * | ||
878 | * - The line offset values are defined as the number of lines (as defined by | ||
879 | * the horizontal line offset) between the start of two consecutive lines for | ||
880 | * all combinations of odd/even lines in odd/even fields. When interleaving | ||
881 | * fields set them all to two lines, and to one line otherwise. | ||
877 | */ | 882 | */ |
878 | static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, | 883 | static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, |
879 | u32 offset, u8 oddeven, u8 numlines) | 884 | unsigned int bpl, |
885 | enum v4l2_field field) | ||
880 | { | 886 | { |
881 | struct isp_device *isp = to_isp_device(ccdc); | 887 | struct isp_device *isp = to_isp_device(ccdc); |
888 | u32 sdofst = 0; | ||
882 | 889 | ||
883 | isp_reg_writel(isp, offset & 0xffff, | 890 | isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC, |
884 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); | 891 | ISPCCDC_HSIZE_OFF); |
885 | |||
886 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
887 | ISPCCDC_SDOFST_FINV); | ||
888 | 892 | ||
889 | isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | 893 | switch (field) { |
890 | ISPCCDC_SDOFST_FOFST_4L); | 894 | case V4L2_FIELD_INTERLACED_TB: |
891 | 895 | case V4L2_FIELD_INTERLACED_BT: | |
892 | switch (oddeven) { | 896 | /* When interleaving fields in memory offset field one by one |
893 | case EVENEVEN: | 897 | * line and set the line offset to two lines. |
894 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | 898 | */ |
895 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); | 899 | sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT) |
896 | break; | 900 | | (1 << ISPCCDC_SDOFST_LOFST1_SHIFT) |
897 | case ODDEVEN: | 901 | | (1 << ISPCCDC_SDOFST_LOFST2_SHIFT) |
898 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | 902 | | (1 << ISPCCDC_SDOFST_LOFST3_SHIFT); |
899 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); | ||
900 | break; | ||
901 | case EVENODD: | ||
902 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
903 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); | ||
904 | break; | ||
905 | case ODDODD: | ||
906 | isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, | ||
907 | (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); | ||
908 | break; | 903 | break; |
904 | |||
909 | default: | 905 | default: |
906 | /* In all other cases set the line offsets to one line. */ | ||
910 | break; | 907 | break; |
911 | } | 908 | } |
909 | |||
910 | isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST); | ||
912 | } | 911 | } |
913 | 912 | ||
914 | /* | 913 | /* |
@@ -1204,7 +1203,17 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) | |||
1204 | << ISPCCDC_VERT_LINES_NLV_SHIFT, | 1203 | << ISPCCDC_VERT_LINES_NLV_SHIFT, |
1205 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); | 1204 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); |
1206 | 1205 | ||
1207 | ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); | 1206 | ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, |
1207 | format->field); | ||
1208 | |||
1209 | /* When interleaving fields enable processing of the field input signal. | ||
1210 | * This will cause the line output control module to apply the field | ||
1211 | * offset to field 1. | ||
1212 | */ | ||
1213 | if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE && | ||
1214 | (format->field == V4L2_FIELD_INTERLACED_TB || | ||
1215 | format->field == V4L2_FIELD_INTERLACED_BT)) | ||
1216 | syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; | ||
1208 | 1217 | ||
1209 | /* The CCDC outputs data in UYVY order by default. Swap bytes to get | 1218 | /* The CCDC outputs data in UYVY order by default. Swap bytes to get |
1210 | * YUYV. | 1219 | * YUYV. |
@@ -1484,6 +1493,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | |||
1484 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); | 1493 | struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); |
1485 | struct isp_device *isp = to_isp_device(ccdc); | 1494 | struct isp_device *isp = to_isp_device(ccdc); |
1486 | struct isp_buffer *buffer; | 1495 | struct isp_buffer *buffer; |
1496 | enum v4l2_field field; | ||
1487 | 1497 | ||
1488 | /* The CCDC generates VD0 interrupts even when disabled (the datasheet | 1498 | /* The CCDC generates VD0 interrupts even when disabled (the datasheet |
1489 | * doesn't explicitly state if that's supposed to happen or not, so it | 1499 | * doesn't explicitly state if that's supposed to happen or not, so it |
@@ -1503,17 +1513,12 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | |||
1503 | return 1; | 1513 | return 1; |
1504 | } | 1514 | } |
1505 | 1515 | ||
1506 | /* When capturing fields in alternate order read the current field | 1516 | /* Read the current field identifier. */ |
1507 | * identifier and store it in the pipeline. | 1517 | field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE) |
1508 | */ | 1518 | & ISPCCDC_SYN_MODE_FLDSTAT |
1509 | if (ccdc->formats[CCDC_PAD_SOURCE_OF].field == V4L2_FIELD_ALTERNATE) { | 1519 | ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; |
1510 | u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, | ||
1511 | ISPCCDC_SYN_MODE); | ||
1512 | |||
1513 | pipe->field = syn_mode & ISPCCDC_SYN_MODE_FLDSTAT | ||
1514 | ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; | ||
1515 | } | ||
1516 | 1520 | ||
1521 | /* Wait for the CCDC to become idle. */ | ||
1517 | if (ccdc_sbl_wait_idle(ccdc, 1000)) { | 1522 | if (ccdc_sbl_wait_idle(ccdc, 1000)) { |
1518 | dev_info(isp->dev, "CCDC won't become idle!\n"); | 1523 | dev_info(isp->dev, "CCDC won't become idle!\n"); |
1519 | isp->crashed |= 1U << ccdc->subdev.entity.id; | 1524 | isp->crashed |= 1U << ccdc->subdev.entity.id; |
@@ -1521,6 +1526,28 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) | |||
1521 | return 0; | 1526 | return 0; |
1522 | } | 1527 | } |
1523 | 1528 | ||
1529 | switch (ccdc->formats[CCDC_PAD_SOURCE_OF].field) { | ||
1530 | case V4L2_FIELD_ALTERNATE: | ||
1531 | /* When capturing fields in alternate order store the current | ||
1532 | * field identifier in the pipeline. | ||
1533 | */ | ||
1534 | pipe->field = field; | ||
1535 | break; | ||
1536 | |||
1537 | case V4L2_FIELD_INTERLACED_TB: | ||
1538 | /* When interleaving fields only complete the buffer after | ||
1539 | * capturing the second field. | ||
1540 | */ | ||
1541 | if (field == V4L2_FIELD_TOP) | ||
1542 | return 1; | ||
1543 | break; | ||
1544 | |||
1545 | case V4L2_FIELD_INTERLACED_BT: | ||
1546 | if (field == V4L2_FIELD_BOTTOM) | ||
1547 | return 1; | ||
1548 | break; | ||
1549 | } | ||
1550 | |||
1524 | buffer = omap3isp_video_buffer_next(&ccdc->video_out); | 1551 | buffer = omap3isp_video_buffer_next(&ccdc->video_out); |
1525 | if (buffer != NULL) | 1552 | if (buffer != NULL) |
1526 | ccdc_set_outaddr(ccdc, buffer->dma); | 1553 | ccdc_set_outaddr(ccdc, buffer->dma); |
@@ -1829,6 +1856,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1829 | unsigned int width = fmt->width; | 1856 | unsigned int width = fmt->width; |
1830 | unsigned int height = fmt->height; | 1857 | unsigned int height = fmt->height; |
1831 | struct v4l2_rect *crop; | 1858 | struct v4l2_rect *crop; |
1859 | enum v4l2_field field; | ||
1832 | unsigned int i; | 1860 | unsigned int i; |
1833 | 1861 | ||
1834 | switch (pad) { | 1862 | switch (pad) { |
@@ -1854,6 +1882,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1854 | 1882 | ||
1855 | case CCDC_PAD_SOURCE_OF: | 1883 | case CCDC_PAD_SOURCE_OF: |
1856 | pixelcode = fmt->code; | 1884 | pixelcode = fmt->code; |
1885 | field = fmt->field; | ||
1857 | *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | 1886 | *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); |
1858 | 1887 | ||
1859 | /* YUV formats are converted from 2X8 to 1X16 by the bridge and | 1888 | /* YUV formats are converted from 2X8 to 1X16 by the bridge and |
@@ -1878,6 +1907,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1878 | crop = __ccdc_get_crop(ccdc, fh, which); | 1907 | crop = __ccdc_get_crop(ccdc, fh, which); |
1879 | fmt->width = crop->width; | 1908 | fmt->width = crop->width; |
1880 | fmt->height = crop->height; | 1909 | fmt->height = crop->height; |
1910 | |||
1911 | /* When input format is interlaced with alternating fields the | ||
1912 | * CCDC can interleave the fields. | ||
1913 | */ | ||
1914 | if (fmt->field == V4L2_FIELD_ALTERNATE && | ||
1915 | (field == V4L2_FIELD_INTERLACED_TB || | ||
1916 | field == V4L2_FIELD_INTERLACED_BT)) { | ||
1917 | fmt->field = field; | ||
1918 | fmt->height *= 2; | ||
1919 | } | ||
1920 | |||
1881 | break; | 1921 | break; |
1882 | 1922 | ||
1883 | case CCDC_PAD_SOURCE_VP: | 1923 | case CCDC_PAD_SOURCE_VP: |
diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h index f37a8df23cf6..b5ea8da0b904 100644 --- a/drivers/media/platform/omap3isp/ispreg.h +++ b/drivers/media/platform/omap3isp/ispreg.h | |||
@@ -730,17 +730,13 @@ | |||
730 | 730 | ||
731 | #define ISPCCDC_HSIZE_OFF_SHIFT 0 | 731 | #define ISPCCDC_HSIZE_OFF_SHIFT 0 |
732 | 732 | ||
733 | #define ISPCCDC_SDOFST_FINV (1 << 14) | 733 | #define ISPCCDC_SDOFST_FIINV (1 << 14) |
734 | #define ISPCCDC_SDOFST_FOFST_1L 0 | 734 | #define ISPCCDC_SDOFST_FOFST_SHIFT 12 |
735 | #define ISPCCDC_SDOFST_FOFST_4L (3 << 12) | 735 | #define ISPCCDC_SDOFST_FOFST_MASK (3 << 12) |
736 | #define ISPCCDC_SDOFST_LOFST3_SHIFT 0 | 736 | #define ISPCCDC_SDOFST_LOFST3_SHIFT 0 |
737 | #define ISPCCDC_SDOFST_LOFST2_SHIFT 3 | 737 | #define ISPCCDC_SDOFST_LOFST2_SHIFT 3 |
738 | #define ISPCCDC_SDOFST_LOFST1_SHIFT 6 | 738 | #define ISPCCDC_SDOFST_LOFST1_SHIFT 6 |
739 | #define ISPCCDC_SDOFST_LOFST0_SHIFT 9 | 739 | #define ISPCCDC_SDOFST_LOFST0_SHIFT 9 |
740 | #define EVENEVEN 1 | ||
741 | #define ODDEVEN 2 | ||
742 | #define EVENODD 3 | ||
743 | #define ODDODD 4 | ||
744 | 740 | ||
745 | #define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 | 741 | #define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 |
746 | #define ISPCCDC_CLAMP_OBST_SHIFT 10 | 742 | #define ISPCCDC_CLAMP_OBST_SHIFT 10 |
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index c38f1d4cc538..bc38c88c7bd9 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c | |||
@@ -637,14 +637,40 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) | |||
637 | if (format->type != video->type) | 637 | if (format->type != video->type) |
638 | return -EINVAL; | 638 | return -EINVAL; |
639 | 639 | ||
640 | /* Default to the progressive field order if the requested value is not | 640 | /* Replace unsupported field orders with sane defaults. */ |
641 | * supported (or set to ANY). The only supported orders are progressive | 641 | switch (format->fmt.pix.field) { |
642 | * (available on all video nodes) and alternate (available on capture | 642 | case V4L2_FIELD_NONE: |
643 | * nodes only). | 643 | /* Progressive is supported everywhere. */ |
644 | */ | 644 | break; |
645 | if (format->fmt.pix.field != V4L2_FIELD_ALTERNATE || | 645 | case V4L2_FIELD_ALTERNATE: |
646 | video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | 646 | /* ALTERNATE is not supported on output nodes. */ |
647 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
648 | format->fmt.pix.field = V4L2_FIELD_NONE; | ||
649 | break; | ||
650 | case V4L2_FIELD_INTERLACED: | ||
651 | /* The ISP has no concept of video standard, select the | ||
652 | * top-bottom order when the unqualified interlaced order is | ||
653 | * requested. | ||
654 | */ | ||
655 | format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; | ||
656 | /* Fall-through */ | ||
657 | case V4L2_FIELD_INTERLACED_TB: | ||
658 | case V4L2_FIELD_INTERLACED_BT: | ||
659 | /* Interlaced orders are only supported at the CCDC output. */ | ||
660 | if (video != &video->isp->isp_ccdc.video_out) | ||
661 | format->fmt.pix.field = V4L2_FIELD_NONE; | ||
662 | break; | ||
663 | case V4L2_FIELD_TOP: | ||
664 | case V4L2_FIELD_BOTTOM: | ||
665 | case V4L2_FIELD_SEQ_TB: | ||
666 | case V4L2_FIELD_SEQ_BT: | ||
667 | default: | ||
668 | /* All other field orders are currently unsupported, default to | ||
669 | * progressive. | ||
670 | */ | ||
647 | format->fmt.pix.field = V4L2_FIELD_NONE; | 671 | format->fmt.pix.field = V4L2_FIELD_NONE; |
672 | break; | ||
673 | } | ||
648 | 674 | ||
649 | /* Fill the bytesperline and sizeimage fields by converting to media bus | 675 | /* Fill the bytesperline and sizeimage fields by converting to media bus |
650 | * format and back to pixel format. | 676 | * format and back to pixel format. |