Commit c51364ca authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

[media] omap3isp: ccdc: Add YUV input formats support

Enable the bridge automatically when the input format is YUYV8 or UYVY8.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 73ea57eb
...@@ -293,7 +293,7 @@ static void isp_core_init(struct isp_device *isp, int idle) ...@@ -293,7 +293,7 @@ static void isp_core_init(struct isp_device *isp, int idle)
void omap3isp_configure_bridge(struct isp_device *isp, void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input, enum ccdc_input_entity input,
const struct isp_parallel_platform_data *pdata, const struct isp_parallel_platform_data *pdata,
unsigned int shift) unsigned int shift, unsigned int bridge)
{ {
u32 ispctrl_val; u32 ispctrl_val;
...@@ -302,12 +302,12 @@ void omap3isp_configure_bridge(struct isp_device *isp, ...@@ -302,12 +302,12 @@ void omap3isp_configure_bridge(struct isp_device *isp,
ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
ispctrl_val |= bridge;
switch (input) { switch (input) {
case CCDC_INPUT_PARALLEL: case CCDC_INPUT_PARALLEL:
ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT;
shift += pdata->data_lane_shift * 2; shift += pdata->data_lane_shift * 2;
break; break;
......
...@@ -236,7 +236,7 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, ...@@ -236,7 +236,7 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
void omap3isp_configure_bridge(struct isp_device *isp, void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input, enum ccdc_input_entity input,
const struct isp_parallel_platform_data *pdata, const struct isp_parallel_platform_data *pdata,
unsigned int shift); unsigned int shift, unsigned int bridge);
struct isp_device *omap3isp_get(struct isp_device *isp); struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp); void omap3isp_put(struct isp_device *isp);
......
...@@ -61,6 +61,8 @@ static const unsigned int ccdc_fmts[] = { ...@@ -61,6 +61,8 @@ static const unsigned int ccdc_fmts[] = {
V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB12_1X12,
V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR12_1X12,
V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG12_1X12,
V4L2_MBUS_FMT_YUYV8_2X8,
V4L2_MBUS_FMT_UYVY8_2X8,
}; };
/* /*
...@@ -973,8 +975,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, ...@@ -973,8 +975,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
unsigned int data_size) unsigned int data_size)
{ {
struct isp_device *isp = to_isp_device(ccdc); struct isp_device *isp = to_isp_device(ccdc);
const struct v4l2_mbus_framefmt *format;
u32 syn_mode = ISPCCDC_SYN_MODE_VDHDEN; u32 syn_mode = ISPCCDC_SYN_MODE_VDHDEN;
format = &ccdc->formats[CCDC_PAD_SINK];
if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
/* The bridge is enabled for YUV8 formats. Configure the input
* mode accordingly.
*/
syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
}
switch (data_size) { switch (data_size) {
case 8: case 8:
syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8;
...@@ -1000,6 +1013,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, ...@@ -1000,6 +1013,19 @@ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
syn_mode |= ISPCCDC_SYN_MODE_VDPOL; syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
/* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
* hardware seems to ignore it in all other input modes.
*/
if (format->code == V4L2_MBUS_FMT_UYVY8_2X8)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS);
else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS);
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
ISPCCDC_REC656IF_R656ON);
} }
/* CCDC formats descriptions */ /* CCDC formats descriptions */
...@@ -1088,6 +1114,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1088,6 +1114,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
unsigned int depth_in = 0; unsigned int depth_in = 0;
struct media_pad *pad; struct media_pad *pad;
unsigned long flags; unsigned long flags;
unsigned int bridge;
unsigned int shift; unsigned int shift;
u32 syn_mode; u32 syn_mode;
u32 ccdc_pattern; u32 ccdc_pattern;
...@@ -1098,7 +1125,9 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1098,7 +1125,9 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
->bus.parallel; ->bus.parallel;
/* Compute shift value for lane shifter to configure the bridge. */ /* Compute the lane shifter shift value and enable the bridge when the
* input format is YUV.
*/
fmt_src.pad = pad->index; fmt_src.pad = pad->index;
fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) { if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
...@@ -1109,14 +1138,18 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1109,14 +1138,18 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
fmt_info = omap3isp_video_format_info fmt_info = omap3isp_video_format_info
(isp->isp_ccdc.formats[CCDC_PAD_SINK].code); (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
depth_out = fmt_info->width; depth_out = fmt_info->width;
shift = depth_in - depth_out; shift = depth_in - depth_out;
omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
ccdc_config_sync_if(ccdc, pdata, depth_out); if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
else
bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
/* CCDC_PAD_SINK */ omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge);
format = &ccdc->formats[CCDC_PAD_SINK];
ccdc_config_sync_if(ccdc, pdata, depth_out);
syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
...@@ -1135,13 +1168,8 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1135,13 +1168,8 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
else else
syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
/* Use PACK8 mode for 1byte per pixel formats. */ /* CCDC_PAD_SINK */
if (omap3isp_video_format_info(format->code)->width <= 8) format = &ccdc->formats[CCDC_PAD_SINK];
syn_mode |= ISPCCDC_SYN_MODE_PACK8;
else
syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
/* Mosaic filter */ /* Mosaic filter */
switch (format->code) { switch (format->code) {
...@@ -1172,6 +1200,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1172,6 +1200,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
/* CCDC_PAD_SOURCE_OF */ /* CCDC_PAD_SOURCE_OF */
format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
crop = &ccdc->crop; crop = &ccdc->crop;
isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
...@@ -1185,6 +1214,24 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1185,6 +1214,24 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
/* The CCDC outputs data in UYVY order by default. Swap bytes to get
* YUYV.
*/
if (format->code == V4L2_MBUS_FMT_YUYV8_1X16)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD);
else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD);
/* Use PACK8 mode for 1byte per pixel formats. */
if (omap3isp_video_format_info(format->code)->width <= 8)
syn_mode |= ISPCCDC_SYN_MODE_PACK8;
else
syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
/* CCDC_PAD_SOURCE_VP */ /* CCDC_PAD_SOURCE_VP */
format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
...@@ -1199,6 +1246,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ...@@ -1199,6 +1246,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
(format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
/* Lens shading correction. */
spin_lock_irqsave(&ccdc->lsc.req_lock, flags); spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
if (ccdc->lsc.request == NULL) if (ccdc->lsc.request == NULL)
goto unlock; goto unlock;
...@@ -1776,8 +1824,8 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1776,8 +1824,8 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
unsigned int pad, struct v4l2_mbus_framefmt *fmt, unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which) enum v4l2_subdev_format_whence which)
{ {
struct v4l2_mbus_framefmt *format;
const struct isp_format_info *info; const struct isp_format_info *info;
enum v4l2_mbus_pixelcode pixelcode;
unsigned int width = fmt->width; unsigned int width = fmt->width;
unsigned int height = fmt->height; unsigned int height = fmt->height;
struct v4l2_rect *crop; struct v4l2_rect *crop;
...@@ -1785,9 +1833,6 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1785,9 +1833,6 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
switch (pad) { switch (pad) {
case CCDC_PAD_SINK: case CCDC_PAD_SINK:
/* TODO: If the CCDC output formatter pad is connected directly
* to the resizer, only YUV formats can be used.
*/
for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) {
if (fmt->code == ccdc_fmts[i]) if (fmt->code == ccdc_fmts[i])
break; break;
...@@ -1803,8 +1848,26 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1803,8 +1848,26 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
break; break;
case CCDC_PAD_SOURCE_OF: case CCDC_PAD_SOURCE_OF:
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); pixelcode = fmt->code;
memcpy(fmt, format, sizeof(*fmt)); *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
/* YUV formats are converted from 2X8 to 1X16 by the bridge and
* can be byte-swapped.
*/
if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) {
/* Use the user requested format if YUV. */
if (pixelcode == V4L2_MBUS_FMT_YUYV8_2X8 ||
pixelcode == V4L2_MBUS_FMT_UYVY8_2X8 ||
pixelcode == V4L2_MBUS_FMT_YUYV8_1X16 ||
pixelcode == V4L2_MBUS_FMT_UYVY8_1X16)
fmt->code = pixelcode;
if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8)
fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
else if (fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
fmt->code = V4L2_MBUS_FMT_UYVY8_1X16;
}
/* Hardcode the output size to the crop rectangle size. */ /* Hardcode the output size to the crop rectangle size. */
crop = __ccdc_get_crop(ccdc, fh, which); crop = __ccdc_get_crop(ccdc, fh, which);
...@@ -1813,13 +1876,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ...@@ -1813,13 +1876,17 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
break; break;
case CCDC_PAD_SOURCE_VP: case CCDC_PAD_SOURCE_VP:
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
memcpy(fmt, format, sizeof(*fmt));
/* The video port interface truncates the data to 10 bits. */ /* The video port interface truncates the data to 10 bits. */
info = omap3isp_video_format_info(fmt->code); info = omap3isp_video_format_info(fmt->code);
fmt->code = info->truncated; fmt->code = info->truncated;
/* YUV formats are not supported by the video port. */
if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
fmt->code = 0;
/* The number of lines that can be clocked out from the video /* The number of lines that can be clocked out from the video
* port output must be at least one line less than the number * port output must be at least one line less than the number
* of input lines. * of input lines.
...@@ -1902,14 +1969,46 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, ...@@ -1902,14 +1969,46 @@ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
break; break;
case CCDC_PAD_SOURCE_OF: case CCDC_PAD_SOURCE_OF:
format = __ccdc_get_format(ccdc, fh, code->pad,
V4L2_SUBDEV_FORMAT_TRY);
if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
/* In YUV mode the CCDC can swap bytes. */
if (code->index == 0)
code->code = V4L2_MBUS_FMT_YUYV8_1X16;
else if (code->index == 1)
code->code = V4L2_MBUS_FMT_UYVY8_1X16;
else
return -EINVAL;
} else {
/* In raw mode, no configurable format confversion is
* available.
*/
if (code->index == 0)
code->code = format->code;
else
return -EINVAL;
}
break;
case CCDC_PAD_SOURCE_VP: case CCDC_PAD_SOURCE_VP:
/* No format conversion inside CCDC */ /* The CCDC supports no configurable format conversion
* compatible with the video port. Enumerate a single output
* format code.
*/
if (code->index != 0) if (code->index != 0)
return -EINVAL; return -EINVAL;
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, format = __ccdc_get_format(ccdc, fh, code->pad,
V4L2_SUBDEV_FORMAT_TRY); V4L2_SUBDEV_FORMAT_TRY);
/* A pixel code equal to 0 means that the video port doesn't
* support the input format. Don't enumerate any pixel code.
*/
if (format->code == 0)
return -EINVAL;
code->code = format->code; code->code = format->code;
break; break;
......
...@@ -120,6 +120,10 @@ static struct isp_format_info formats[] = { ...@@ -120,6 +120,10 @@ static struct isp_format_info formats[] = {
{ V4L2_MBUS_FMT_YUYV8_2X8, V4L2_MBUS_FMT_YUYV8_2X8, { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_MBUS_FMT_YUYV8_2X8,
V4L2_MBUS_FMT_YUYV8_2X8, 0, V4L2_MBUS_FMT_YUYV8_2X8, 0,
V4L2_PIX_FMT_YUYV, 8, 2, }, V4L2_PIX_FMT_YUYV, 8, 2, },
/* Empty entry to catch the unsupported pixel code (0) used by the CCDC
* module and avoid NULL pointer dereferences.
*/
{ 0, }
}; };
const struct isp_format_info * const struct isp_format_info *
......
...@@ -41,12 +41,6 @@ enum isp_interface_type { ...@@ -41,12 +41,6 @@ enum isp_interface_type {
ISP_INTERFACE_CSI2C_PHY1, ISP_INTERFACE_CSI2C_PHY1,
}; };
enum {
ISP_BRIDGE_DISABLE = 0,
ISP_BRIDGE_LITTLE_ENDIAN = 2,
ISP_BRIDGE_BIG_ENDIAN = 3,
};
enum { enum {
ISP_LANE_SHIFT_0 = 0, ISP_LANE_SHIFT_0 = 0,
ISP_LANE_SHIFT_2 = 1, ISP_LANE_SHIFT_2 = 1,
...@@ -69,10 +63,6 @@ enum { ...@@ -69,10 +63,6 @@ enum {
* 0 - Active high, 1 - Active low * 0 - Active high, 1 - Active low
* @data_pol: Data polarity * @data_pol: Data polarity
* 0 - Normal, 1 - One's complement * 0 - Normal, 1 - One's complement
* @bridge: CCDC Bridge input control
* ISP_BRIDGE_DISABLE - Disable
* ISP_BRIDGE_LITTLE_ENDIAN - Little endian
* ISP_BRIDGE_BIG_ENDIAN - Big endian
*/ */
struct isp_parallel_platform_data { struct isp_parallel_platform_data {
unsigned int data_lane_shift:2; unsigned int data_lane_shift:2;
...@@ -80,7 +70,6 @@ struct isp_parallel_platform_data { ...@@ -80,7 +70,6 @@ struct isp_parallel_platform_data {
unsigned int hs_pol:1; unsigned int hs_pol:1;
unsigned int vs_pol:1; unsigned int vs_pol:1;
unsigned int data_pol:1; unsigned int data_pol:1;
unsigned int bridge:2;
}; };
enum { enum {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment