aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2014-05-19 18:40:04 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-08-21 16:25:14 -0400
commitbcb4e0efd1380d93866df51ec5d8dfaa026537ad (patch)
treedc52f14b55b69cea7d8eb283edd9e48d2e9df768
parent9a36d8ed33c481a99f69f8a2eeb22e3c7750e522 (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.c132
-rw-r--r--drivers/media/platform/omap3isp/ispreg.h10
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c40
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 */
878static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, 883static 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.