diff options
-rw-r--r-- | drivers/media/video/omap3isp/ispccdc.c | 180 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispccdc.h | 2 |
2 files changed, 169 insertions, 13 deletions
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 17207c7037f7..8c73197005c6 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c | |||
@@ -38,6 +38,9 @@ | |||
38 | #include "ispreg.h" | 38 | #include "ispreg.h" |
39 | #include "ispccdc.h" | 39 | #include "ispccdc.h" |
40 | 40 | ||
41 | #define CCDC_MIN_WIDTH 32 | ||
42 | #define CCDC_MIN_HEIGHT 32 | ||
43 | |||
41 | static struct v4l2_mbus_framefmt * | 44 | static struct v4l2_mbus_framefmt * |
42 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | 45 | __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, |
43 | unsigned int pad, enum v4l2_subdev_format_whence which); | 46 | unsigned int pad, enum v4l2_subdev_format_whence which); |
@@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) | |||
1118 | struct isp_parallel_platform_data *pdata = NULL; | 1121 | struct isp_parallel_platform_data *pdata = NULL; |
1119 | struct v4l2_subdev *sensor; | 1122 | struct v4l2_subdev *sensor; |
1120 | struct v4l2_mbus_framefmt *format; | 1123 | struct v4l2_mbus_framefmt *format; |
1124 | const struct v4l2_rect *crop; | ||
1121 | const struct isp_format_info *fmt_info; | 1125 | const struct isp_format_info *fmt_info; |
1122 | struct v4l2_subdev_format fmt_src; | 1126 | struct v4l2_subdev_format fmt_src; |
1123 | unsigned int depth_out; | 1127 | unsigned int depth_out; |
@@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) | |||
1211 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); | 1215 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); |
1212 | 1216 | ||
1213 | /* CCDC_PAD_SOURCE_OF */ | 1217 | /* CCDC_PAD_SOURCE_OF */ |
1214 | format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; | 1218 | crop = &ccdc->crop; |
1215 | 1219 | ||
1216 | isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | | 1220 | isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | |
1217 | ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), | 1221 | ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), |
1218 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); | 1222 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); |
1219 | isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, | 1223 | isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, |
1220 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); | 1224 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); |
1221 | isp_reg_writel(isp, (format->height - 1) | 1225 | isp_reg_writel(isp, (crop->height - 1) |
1222 | << ISPCCDC_VERT_LINES_NLV_SHIFT, | 1226 | << ISPCCDC_VERT_LINES_NLV_SHIFT, |
1223 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); | 1227 | OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); |
1224 | 1228 | ||
@@ -1793,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1793 | return &ccdc->formats[pad]; | 1797 | return &ccdc->formats[pad]; |
1794 | } | 1798 | } |
1795 | 1799 | ||
1800 | static struct v4l2_rect * | ||
1801 | __ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | ||
1802 | enum v4l2_subdev_format_whence which) | ||
1803 | { | ||
1804 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
1805 | return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); | ||
1806 | else | ||
1807 | return &ccdc->crop; | ||
1808 | } | ||
1809 | |||
1796 | /* | 1810 | /* |
1797 | * ccdc_try_format - Try video format on a pad | 1811 | * ccdc_try_format - Try video format on a pad |
1798 | * @ccdc: ISP CCDC device | 1812 | * @ccdc: ISP CCDC device |
@@ -1809,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1809 | const struct isp_format_info *info; | 1823 | const struct isp_format_info *info; |
1810 | unsigned int width = fmt->width; | 1824 | unsigned int width = fmt->width; |
1811 | unsigned int height = fmt->height; | 1825 | unsigned int height = fmt->height; |
1826 | struct v4l2_rect *crop; | ||
1812 | unsigned int i; | 1827 | unsigned int i; |
1813 | 1828 | ||
1814 | switch (pad) { | 1829 | switch (pad) { |
@@ -1834,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1834 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); | 1849 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); |
1835 | memcpy(fmt, format, sizeof(*fmt)); | 1850 | memcpy(fmt, format, sizeof(*fmt)); |
1836 | 1851 | ||
1837 | /* The data formatter truncates the number of horizontal output | 1852 | /* Hardcode the output size to the crop rectangle size. */ |
1838 | * pixels to a multiple of 16. To avoid clipping data, allow | 1853 | crop = __ccdc_get_crop(ccdc, fh, which); |
1839 | * callers to request an output size bigger than the input size | 1854 | fmt->width = crop->width; |
1840 | * up to the nearest multiple of 16. | 1855 | fmt->height = crop->height; |
1841 | */ | ||
1842 | fmt->width = clamp_t(u32, width, 32, fmt->width + 15); | ||
1843 | fmt->width &= ~15; | ||
1844 | fmt->height = clamp_t(u32, height, 32, fmt->height); | ||
1845 | break; | 1856 | break; |
1846 | 1857 | ||
1847 | case CCDC_PAD_SOURCE_VP: | 1858 | case CCDC_PAD_SOURCE_VP: |
@@ -1869,6 +1880,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, | |||
1869 | } | 1880 | } |
1870 | 1881 | ||
1871 | /* | 1882 | /* |
1883 | * ccdc_try_crop - Validate a crop rectangle | ||
1884 | * @ccdc: ISP CCDC device | ||
1885 | * @sink: format on the sink pad | ||
1886 | * @crop: crop rectangle to be validated | ||
1887 | */ | ||
1888 | static void ccdc_try_crop(struct isp_ccdc_device *ccdc, | ||
1889 | const struct v4l2_mbus_framefmt *sink, | ||
1890 | struct v4l2_rect *crop) | ||
1891 | { | ||
1892 | const struct isp_format_info *info; | ||
1893 | unsigned int max_width; | ||
1894 | |||
1895 | /* For Bayer formats, restrict left/top and width/height to even values | ||
1896 | * to keep the Bayer pattern. | ||
1897 | */ | ||
1898 | info = omap3isp_video_format_info(sink->code); | ||
1899 | if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { | ||
1900 | crop->left &= ~1; | ||
1901 | crop->top &= ~1; | ||
1902 | } | ||
1903 | |||
1904 | crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH); | ||
1905 | crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT); | ||
1906 | |||
1907 | /* The data formatter truncates the number of horizontal output pixels | ||
1908 | * to a multiple of 16. To avoid clipping data, allow callers to request | ||
1909 | * an output size bigger than the input size up to the nearest multiple | ||
1910 | * of 16. | ||
1911 | */ | ||
1912 | max_width = (sink->width - crop->left + 15) & ~15; | ||
1913 | crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width) | ||
1914 | & ~15; | ||
1915 | crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT, | ||
1916 | sink->height - crop->top); | ||
1917 | |||
1918 | /* Odd width/height values don't make sense for Bayer formats. */ | ||
1919 | if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { | ||
1920 | crop->width &= ~1; | ||
1921 | crop->height &= ~1; | ||
1922 | } | ||
1923 | } | ||
1924 | |||
1925 | /* | ||
1872 | * ccdc_enum_mbus_code - Handle pixel format enumeration | 1926 | * ccdc_enum_mbus_code - Handle pixel format enumeration |
1873 | * @sd : pointer to v4l2 subdev structure | 1927 | * @sd : pointer to v4l2 subdev structure |
1874 | * @fh : V4L2 subdev file handle | 1928 | * @fh : V4L2 subdev file handle |
@@ -1940,6 +1994,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, | |||
1940 | } | 1994 | } |
1941 | 1995 | ||
1942 | /* | 1996 | /* |
1997 | * ccdc_get_selection - Retrieve a selection rectangle on a pad | ||
1998 | * @sd: ISP CCDC V4L2 subdevice | ||
1999 | * @fh: V4L2 subdev file handle | ||
2000 | * @sel: Selection rectangle | ||
2001 | * | ||
2002 | * The only supported rectangles are the crop rectangles on the output formatter | ||
2003 | * source pad. | ||
2004 | * | ||
2005 | * Return 0 on success or a negative error code otherwise. | ||
2006 | */ | ||
2007 | static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
2008 | struct v4l2_subdev_selection *sel) | ||
2009 | { | ||
2010 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
2011 | struct v4l2_mbus_framefmt *format; | ||
2012 | |||
2013 | if (sel->pad != CCDC_PAD_SOURCE_OF) | ||
2014 | return -EINVAL; | ||
2015 | |||
2016 | switch (sel->target) { | ||
2017 | case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: | ||
2018 | sel->r.left = 0; | ||
2019 | sel->r.top = 0; | ||
2020 | sel->r.width = INT_MAX; | ||
2021 | sel->r.height = INT_MAX; | ||
2022 | |||
2023 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); | ||
2024 | ccdc_try_crop(ccdc, format, &sel->r); | ||
2025 | break; | ||
2026 | |||
2027 | case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: | ||
2028 | sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); | ||
2029 | break; | ||
2030 | |||
2031 | default: | ||
2032 | return -EINVAL; | ||
2033 | } | ||
2034 | |||
2035 | return 0; | ||
2036 | } | ||
2037 | |||
2038 | /* | ||
2039 | * ccdc_set_selection - Set a selection rectangle on a pad | ||
2040 | * @sd: ISP CCDC V4L2 subdevice | ||
2041 | * @fh: V4L2 subdev file handle | ||
2042 | * @sel: Selection rectangle | ||
2043 | * | ||
2044 | * The only supported rectangle is the actual crop rectangle on the output | ||
2045 | * formatter source pad. | ||
2046 | * | ||
2047 | * Return 0 on success or a negative error code otherwise. | ||
2048 | */ | ||
2049 | static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
2050 | struct v4l2_subdev_selection *sel) | ||
2051 | { | ||
2052 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | ||
2053 | struct v4l2_mbus_framefmt *format; | ||
2054 | |||
2055 | if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || | ||
2056 | sel->pad != CCDC_PAD_SOURCE_OF) | ||
2057 | return -EINVAL; | ||
2058 | |||
2059 | /* The crop rectangle can't be changed while streaming. */ | ||
2060 | if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) | ||
2061 | return -EBUSY; | ||
2062 | |||
2063 | /* Modifying the crop rectangle always changes the format on the source | ||
2064 | * pad. If the KEEP_CONFIG flag is set, just return the current crop | ||
2065 | * rectangle. | ||
2066 | */ | ||
2067 | if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { | ||
2068 | sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); | ||
2069 | return 0; | ||
2070 | } | ||
2071 | |||
2072 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); | ||
2073 | ccdc_try_crop(ccdc, format, &sel->r); | ||
2074 | *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; | ||
2075 | |||
2076 | /* Update the source format. */ | ||
2077 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); | ||
2078 | ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); | ||
2079 | |||
2080 | return 0; | ||
2081 | } | ||
2082 | |||
2083 | /* | ||
1943 | * ccdc_get_format - Retrieve the video format on a pad | 2084 | * ccdc_get_format - Retrieve the video format on a pad |
1944 | * @sd : ISP CCDC V4L2 subdevice | 2085 | * @sd : ISP CCDC V4L2 subdevice |
1945 | * @fh : V4L2 subdev file handle | 2086 | * @fh : V4L2 subdev file handle |
@@ -1976,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |||
1976 | { | 2117 | { |
1977 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); | 2118 | struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); |
1978 | struct v4l2_mbus_framefmt *format; | 2119 | struct v4l2_mbus_framefmt *format; |
2120 | struct v4l2_rect *crop; | ||
1979 | 2121 | ||
1980 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); | 2122 | format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); |
1981 | if (format == NULL) | 2123 | if (format == NULL) |
@@ -1986,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |||
1986 | 2128 | ||
1987 | /* Propagate the format from sink to source */ | 2129 | /* Propagate the format from sink to source */ |
1988 | if (fmt->pad == CCDC_PAD_SINK) { | 2130 | if (fmt->pad == CCDC_PAD_SINK) { |
2131 | /* Reset the crop rectangle. */ | ||
2132 | crop = __ccdc_get_crop(ccdc, fh, fmt->which); | ||
2133 | crop->left = 0; | ||
2134 | crop->top = 0; | ||
2135 | crop->width = fmt->format.width; | ||
2136 | crop->height = fmt->format.height; | ||
2137 | |||
2138 | ccdc_try_crop(ccdc, &fmt->format, crop); | ||
2139 | |||
2140 | /* Update the source formats. */ | ||
1989 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, | 2141 | format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, |
1990 | fmt->which); | 2142 | fmt->which); |
1991 | *format = fmt->format; | 2143 | *format = fmt->format; |
@@ -2044,6 +2196,8 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { | |||
2044 | .enum_frame_size = ccdc_enum_frame_size, | 2196 | .enum_frame_size = ccdc_enum_frame_size, |
2045 | .get_fmt = ccdc_get_format, | 2197 | .get_fmt = ccdc_get_format, |
2046 | .set_fmt = ccdc_set_format, | 2198 | .set_fmt = ccdc_set_format, |
2199 | .get_selection = ccdc_get_selection, | ||
2200 | .set_selection = ccdc_set_selection, | ||
2047 | }; | 2201 | }; |
2048 | 2202 | ||
2049 | /* V4L2 subdev operations */ | 2203 | /* V4L2 subdev operations */ |
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h index 6d0264bab75b..966bbf8a1262 100644 --- a/drivers/media/video/omap3isp/ispccdc.h +++ b/drivers/media/video/omap3isp/ispccdc.h | |||
@@ -147,6 +147,7 @@ struct ispccdc_lsc { | |||
147 | * @subdev: V4L2 subdevice | 147 | * @subdev: V4L2 subdevice |
148 | * @pads: Sink and source media entity pads | 148 | * @pads: Sink and source media entity pads |
149 | * @formats: Active video formats | 149 | * @formats: Active video formats |
150 | * @crop: Active crop rectangle on the OF source pad | ||
150 | * @input: Active input | 151 | * @input: Active input |
151 | * @output: Active outputs | 152 | * @output: Active outputs |
152 | * @video_out: Output video node | 153 | * @video_out: Output video node |
@@ -173,6 +174,7 @@ struct isp_ccdc_device { | |||
173 | struct v4l2_subdev subdev; | 174 | struct v4l2_subdev subdev; |
174 | struct media_pad pads[CCDC_PADS_NUM]; | 175 | struct media_pad pads[CCDC_PADS_NUM]; |
175 | struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; | 176 | struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; |
177 | struct v4l2_rect crop; | ||
176 | 178 | ||
177 | enum ccdc_input_entity input; | 179 | enum ccdc_input_entity input; |
178 | unsigned int output; | 180 | unsigned int output; |