The video output format can freely be chosen to be 24-bit SRGB or 16-bit YUV 4:2:2 in either SMPTE170M or REC709 color space. In all three modes the output can be full or limited range. Signed-off-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> --- drivers/media/i2c/tc358743.c | 118 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 64a97bbbd00a8..817741e20cc16 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -96,6 +96,7 @@ struct tc358743_state { struct v4l2_dv_timings timings; u32 mbus_fmt_code; + u8 vout_color_sel; u8 csi_lanes_in_use; struct gpio_desc *reset_gpio; @@ -620,6 +621,7 @@ static void tc358743_set_ref_clk(struct v4l2_subdev *sd) static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) { struct tc358743_state *state = to_state(sd); + u8 vout_color_sel = state->vout_color_sel; switch (state->mbus_fmt_code) { case MEDIA_BUS_FMT_UYVY8_1X16: @@ -627,8 +629,17 @@ static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) i2c_wr8_and_or(sd, VOUT_SET2, ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, MASK_SEL422 | MASK_VOUT_422FIL_100); + switch (vout_color_sel) { + case MASK_VOUT_COLOR_601_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_LIMITED: + break; + default: + vout_color_sel = MASK_VOUT_COLOR_601_YCBCR_LIMITED; + break; + } i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, - MASK_VOUT_COLOR_601_YCBCR_LIMITED); + vout_color_sel); mutex_lock(&state->confctl_mutex); i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, MASK_YCBCRFMT_422_8_BIT); @@ -639,8 +650,10 @@ static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) i2c_wr8_and_or(sd, VOUT_SET2, ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, 0x00); + if (vout_color_sel != MASK_VOUT_COLOR_RGB_LIMITED) + vout_color_sel = MASK_VOUT_COLOR_RGB_FULL; i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, - MASK_VOUT_COLOR_RGB_FULL); + vout_color_sel); mutex_lock(&state->confctl_mutex); i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); mutex_unlock(&state->confctl_mutex); @@ -1096,11 +1109,17 @@ static int tc358743_log_status(struct v4l2_subdev *sd) uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); uint16_t sysctl = i2c_rd16(sd, SYSCTL); u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); + u8 vi_rep = i2c_rd8(sd, VI_REP); const int deep_color_mode[4] = { 8, 10, 12, 16 }; static const char * const input_color_space[] = { "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; + static const char * const output_color_space[8] = { + "full range (0-255)", "limited range (16-235)", + "Bt.601 (0-255)", "Bt.601 (16-235)", + "Bt.709 (0-255)", "Bt.709 (16-235)", + "full to limited range", "limited to full range"}; v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip ID: 0x%02x\n", @@ -1159,11 +1178,12 @@ static int tc358743_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Stopped: %s\n", (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? "yes" : "no"); - v4l2_info(sd, "Color space: %s\n", + v4l2_info(sd, "Color space: %s %s\n", state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? "YCbCr 422 16-bit" : state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? - "RGB 888 24-bit" : "Unsupported"); + "RGB 888 24-bit" : "Unsupported", + output_color_space[(vi_rep & MASK_VOUT_COLOR_SEL) >> 5]); v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); v4l2_info(sd, "HDCP encrypted content: %s\n", @@ -1486,16 +1506,40 @@ static int tc358743_get_fmt(struct v4l2_subdev *sd, switch (vi_rep & MASK_VOUT_COLOR_SEL) { case MASK_VOUT_COLOR_RGB_FULL: + format->format.colorspace = V4L2_COLORSPACE_SRGB; + format->format.xfer_func = V4L2_XFER_FUNC_SRGB; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + format->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; case MASK_VOUT_COLOR_RGB_LIMITED: format->format.colorspace = V4L2_COLORSPACE_SRGB; + format->format.xfer_func = V4L2_XFER_FUNC_SRGB; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + format->format.quantization = V4L2_QUANTIZATION_LIM_RANGE; break; case MASK_VOUT_COLOR_601_YCBCR_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->format.xfer_func = V4L2_XFER_FUNC_709; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + format->format.quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; case MASK_VOUT_COLOR_601_YCBCR_FULL: format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->format.xfer_func = V4L2_XFER_FUNC_709; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_XV601; + format->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; break; case MASK_VOUT_COLOR_709_YCBCR_FULL: + format->format.colorspace = V4L2_COLORSPACE_REC709; + format->format.xfer_func = V4L2_XFER_FUNC_709; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_XV709; + format->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; case MASK_VOUT_COLOR_709_YCBCR_LIMITED: format->format.colorspace = V4L2_COLORSPACE_REC709; + format->format.xfer_func = V4L2_XFER_FUNC_709; + format->format.ycbcr_enc = V4L2_YCBCR_ENC_709; + format->format.quantization = V4L2_QUANTIZATION_LIM_RANGE; break; default: format->format.colorspace = 0; @@ -1512,7 +1556,11 @@ static int tc358743_set_fmt(struct v4l2_subdev *sd, struct tc358743_state *state = to_state(sd); u32 code = format->format.code; /* is overwritten by get_fmt */ + enum v4l2_colorspace colorspace = format->format.colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc = format->format.ycbcr_enc; + enum v4l2_quantization quantization = format->format.quantization; int ret = tc358743_get_fmt(sd, cfg, format); + u8 vout_color_sel; format->format.code = code; @@ -1521,16 +1569,78 @@ static int tc358743_set_fmt(struct v4l2_subdev *sd, switch (code) { case MEDIA_BUS_FMT_RGB888_1X24: + colorspace = V4L2_COLORSPACE_SRGB; + break; case MEDIA_BUS_FMT_UYVY8_1X16: + switch (colorspace) { + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_REC709: + break; + default: + if (format->format.colorspace != V4L2_COLORSPACE_SRGB) + colorspace = format->format.colorspace; + else + colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + } + break; + default: + return -EINVAL; + } + + format->format.colorspace = colorspace; + + if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace); + if (quantization == V4L2_QUANTIZATION_DEFAULT) + quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false, colorspace, + ycbcr_enc); + + switch (colorspace) { + case V4L2_COLORSPACE_SRGB: + format->format.xfer_func = V4L2_XFER_FUNC_SRGB; + ycbcr_enc = V4L2_YCBCR_ENC_601; + if (quantization == V4L2_QUANTIZATION_LIM_RANGE) { + vout_color_sel = MASK_VOUT_COLOR_RGB_LIMITED; + } else { + quantization = V4L2_QUANTIZATION_FULL_RANGE; + vout_color_sel = MASK_VOUT_COLOR_RGB_FULL; + } + break; + case V4L2_COLORSPACE_SMPTE170M: + format->format.xfer_func = V4L2_XFER_FUNC_709; + if (quantization == V4L2_QUANTIZATION_FULL_RANGE) { + ycbcr_enc = V4L2_YCBCR_ENC_XV601; + vout_color_sel = MASK_VOUT_COLOR_601_YCBCR_FULL; + } else { + ycbcr_enc = V4L2_YCBCR_ENC_601; + quantization = V4L2_QUANTIZATION_LIM_RANGE; + vout_color_sel = MASK_VOUT_COLOR_601_YCBCR_LIMITED; + } + break; + case V4L2_COLORSPACE_REC709: + format->format.xfer_func = V4L2_XFER_FUNC_709; + if (quantization == V4L2_QUANTIZATION_FULL_RANGE) { + ycbcr_enc = V4L2_YCBCR_ENC_XV709; + vout_color_sel = MASK_VOUT_COLOR_709_YCBCR_FULL; + } else { + ycbcr_enc = V4L2_YCBCR_ENC_709; + quantization = V4L2_QUANTIZATION_LIM_RANGE; + vout_color_sel = MASK_VOUT_COLOR_709_YCBCR_LIMITED; + } break; default: return -EINVAL; } + format->format.ycbcr_enc = ycbcr_enc; + format->format.quantization = quantization; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; state->mbus_fmt_code = format->format.code; + state->vout_color_sel = vout_color_sel; enable_stream(sd, false); tc358743_set_pll(sd); -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html