drm/rockchip: dw-hdmi: Support hdmi quantization range setting

Adding hdmi quantization range switching function. The current
version use default quant range, subsequent version will keep consistent
with the kernel Setting.

Change-Id: Ibb93f7c08d72322caa15f12b1d6e1f901371b27b
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
This commit is contained in:
Algea Cao 2019-03-21 10:23:30 +08:00 committed by Jianhong Chen
parent af4fa70793
commit b5016cf2d5
5 changed files with 121 additions and 5 deletions

View File

@ -2748,6 +2748,24 @@ static void drm_parse_hdmi_forum_vsdb(struct hdmi_edid_data *data,
drm_parse_ycbcr420_deep_color_info(data, hf_vsdb);
}
/**
* drm_default_rgb_quant_range - default RGB quantization range
* @mode: display mode
*
* Determine the default RGB quantization range for the mode,
* as specified in CEA-861.
*
* Return: The default RGB quantization range for the mode
*/
enum hdmi_quantization_range
drm_default_rgb_quant_range(struct drm_display_mode *mode)
{
/* All CEA modes other than VIC 1 use limited quantization range. */
return drm_match_cea_mode(mode) > 1 ?
HDMI_QUANTIZATION_RANGE_LIMITED :
HDMI_QUANTIZATION_RANGE_FULL;
}
static void drm_parse_hdmi_deep_color_info(struct hdmi_edid_data *data,
const u8 *hdmi)
{
@ -5157,6 +5175,50 @@ static int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
return 0;
}
/**
* drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
* quantization range information
* @frame: HDMI AVI infoframe
* @rgb_quant_range: RGB quantization range (Q)
* @rgb_quant_range_selectable: Sink support selectable RGB quantization range (QS)
*/
void
drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
struct drm_display_mode *mode,
enum hdmi_quantization_range rgb_quant_range,
bool rgb_quant_range_selectable)
{
/*
* CEA-861:
* "A Source shall not send a non-zero Q value that does not correspond
* to the default RGB Quantization Range for the transmitted Picture
* unless the Sink indicates support for the Q bit in a Video
* Capabilities Data Block."
*
* HDMI 2.0 recommends sending non-zero Q when it does match the
* default RGB quantization range for the mode, even when QS=0.
*/
if (rgb_quant_range_selectable ||
rgb_quant_range == drm_default_rgb_quant_range(mode))
frame->quantization_range = rgb_quant_range;
else
frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
/*
* CEA-861-F:
* "When transmitting any RGB colorimetry, the Source should set the
* YQ-field to match the RGB Quantization Range being transmitted
* (e.g., when Limited Range RGB, set YQ=0 or when Full Range RGB,
* set YQ=1) and the Sink shall ignore the YQ-field."
*/
if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED)
frame->ycc_quantization_range =
HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
else
frame->ycc_quantization_range =
HDMI_YCC_QUANTIZATION_RANGE_FULL;
}
static enum hdmi_3d_structure
s3d_structure_from_display_mode(const struct drm_display_mode *mode)
{

View File

@ -111,6 +111,12 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
{ 0x6756, 0x78ab, 0x2000, 0x0200 }
};
static const u16 csc_coeff_full_to_limited[3][4] = {
{ 0x36f7, 0x0000, 0x0000, 0x0040 },
{ 0x0000, 0x36f7, 0x0000, 0x0040 },
{ 0x0000, 0x0000, 0x36f7, 0x0040 }
};
struct hdmi_vmode {
bool mdataenablepolarity;
@ -125,6 +131,7 @@ struct hdmi_data_info {
unsigned int enc_out_bus_format;
unsigned int enc_in_encoding;
unsigned int enc_out_encoding;
unsigned int quant_range;
unsigned int pix_repet_factor;
struct hdmi_vmode video_mode;
};
@ -327,8 +334,26 @@ static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
static int is_color_space_conversion(struct dw_hdmi *hdmi)
{
return hdmi->hdmi_data.enc_in_bus_format !=
hdmi->hdmi_data.enc_out_bus_format;
struct drm_display_mode *mode =
hdmi->edid_data.preferred_mode;
bool is_cea_default;
is_cea_default = (drm_match_cea_mode(mode) > 1) &&
(hdmi->hdmi_data.quant_range ==
HDMI_QUANTIZATION_RANGE_DEFAULT);
/*
* When output is rgb limited range or default range with
* cea mode, csc should be enabled.
*/
if (hdmi->hdmi_data.enc_in_bus_format !=
hdmi->hdmi_data.enc_out_bus_format ||
((hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
is_cea_default) &&
hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format)))
return 1;
return 0;
}
static int is_color_space_decimation(struct dw_hdmi *hdmi)
@ -1139,16 +1164,22 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
unsigned i;
u32 csc_scale = 1;
int enc_out_rgb, enc_in_rgb;
enc_out_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format);
enc_in_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format);
if (is_color_space_conversion(hdmi)) {
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
if (enc_out_rgb == enc_in_rgb) {
csc_coeff = &csc_coeff_full_to_limited;
csc_scale = 0;
} else if (enc_out_rgb) {
if (hdmi->hdmi_data.enc_out_encoding ==
V4L2_YCBCR_ENC_601)
csc_coeff = &csc_coeff_rgb_out_eitu601;
else
csc_coeff = &csc_coeff_rgb_out_eitu709;
} else if (hdmi_bus_fmt_is_rgb(
hdmi->hdmi_data.enc_in_bus_format)) {
} else if (enc_in_rgb) {
if (hdmi->hdmi_data.enc_out_encoding ==
V4L2_YCBCR_ENC_601)
csc_coeff = &csc_coeff_rgb_in_eitu601;
@ -1513,6 +1544,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
struct hdmi_avi_infoframe frame;
u8 val;
bool is_hdmi2 = false;
enum hdmi_quantization_range rgb_quant_range =
hdmi->hdmi_data.quant_range;
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi->edid_data.display_info.hdmi.scdc.supported)
@ -1520,6 +1553,12 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
/* Initialise info frame from DRM mode */
drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2);
/*
* Ignore monitor selectable quantization, use quantization set
* by the user
*/
drm_hdmi_avi_infoframe_quant_range(&frame, mode, rgb_quant_range,
true);
if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
frame.colorspace = HDMI_COLORSPACE_YUV444;
else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
@ -2034,6 +2073,13 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
hdmi->plat_data->input_bus_encoding;
else
hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
if (hdmi->plat_data->get_quant_range)
hdmi->hdmi_data.quant_range =
hdmi->plat_data->get_quant_range(data);
else
hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
/*
* According to the dw-hdmi specification 6.4.2
* vp_pr_cd[3:0]:

View File

@ -847,6 +847,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid);
bool drm_detect_monitor_audio(struct edid *edid);
int do_cea_modes(struct hdmi_edid_data *data, const u8 *db, u8 len);
int drm_do_get_edid(struct ddc_adapter *adap, u8 *edid);
enum hdmi_quantization_range
drm_default_rgb_quant_range(struct drm_display_mode *mode);
u8 drm_scdc_readb(struct ddc_adapter *adap, u8 offset,
u8 *value);
u8 drm_scdc_writeb(struct ddc_adapter *adap, u8 offset,

View File

@ -163,6 +163,7 @@ struct dw_hdmi_plat_data {
unsigned long (*get_output_bus_format)(void *data);
unsigned long (*get_enc_in_encoding)(void *data);
unsigned long (*get_enc_out_encoding)(void *data);
unsigned long (*get_quant_range)(void *data);
};
#endif /* __IMX_HDMI_H__ */

View File

@ -322,6 +322,11 @@ int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
struct drm_display_mode *mode,
bool is_hdmi2_sink);
void
drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
struct drm_display_mode *mode,
enum hdmi_quantization_range rgb_quant_range,
bool rgb_quant_range_selectable);
u8 drm_match_cea_mode(struct drm_display_mode *to_match);
#endif /* _DRM_HDMI_H */