drm: edid: add support for parsing additional EDID blocks
Currently the DRM upstream EDID parser doesn't have support to parse all EDID blocks such as extension tag blocks. Add support for parsing these blocks and extract necessary info. Change-Id: Iae92de79960f6f0e73a8e2ff7944c1bf101d90a6 Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
This commit is contained in:
parent
89ed892b18
commit
31604f1c6f
3 changed files with 258 additions and 3 deletions
|
@ -89,6 +89,14 @@ struct detailed_mode_closure {
|
|||
#define LEVEL_GTF2 2
|
||||
#define LEVEL_CVT 3
|
||||
|
||||
/*Enum storing luminance types for HDR blocks in EDID*/
|
||||
enum luminance_value {
|
||||
NO_LUMINANCE_DATA = 3,
|
||||
MAXIMUM_LUMINANCE = 4,
|
||||
FRAME_AVERAGE_LUMINANCE = 5,
|
||||
MINIMUM_LUMINANCE = 6
|
||||
};
|
||||
|
||||
static struct edid_quirk {
|
||||
char vendor[4];
|
||||
int product_id;
|
||||
|
@ -2697,12 +2705,15 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
|
|||
|
||||
return closure.modes;
|
||||
}
|
||||
|
||||
#define VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK 0x0
|
||||
#define AUDIO_BLOCK 0x01
|
||||
#define VIDEO_BLOCK 0x02
|
||||
#define VENDOR_BLOCK 0x03
|
||||
#define SPEAKER_BLOCK 0x04
|
||||
#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06
|
||||
#define EXTENDED_TAG 0x07
|
||||
#define VIDEO_CAPABILITY_BLOCK 0x07
|
||||
#define Y420_VIDEO_DATA_BLOCK 0x0E
|
||||
#define EDID_BASIC_AUDIO (1 << 6)
|
||||
#define EDID_CEA_YCRCB444 (1 << 5)
|
||||
#define EDID_CEA_YCRCB422 (1 << 4)
|
||||
|
@ -3459,6 +3470,227 @@ parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
|
|||
"supported" : "not supported");
|
||||
}
|
||||
|
||||
static void
|
||||
drm_hdmi_extract_vsdbs_info(struct drm_connector *connector, struct edid *edid)
|
||||
{
|
||||
const u8 *cea = drm_find_cea_extension(edid);
|
||||
const u8 *db = NULL;
|
||||
|
||||
if (cea && cea_revision(cea) >= 3) {
|
||||
int i, start, end;
|
||||
|
||||
if (cea_db_offsets(cea, &start, &end))
|
||||
return;
|
||||
|
||||
for_each_cea_db(cea, i, start, end) {
|
||||
db = &cea[i];
|
||||
|
||||
if (cea_db_tag(db) == VENDOR_BLOCK) {
|
||||
/* HDMI Vendor-Specific Data Block */
|
||||
if (cea_db_is_hdmi_vsdb(db))
|
||||
parse_hdmi_vsdb(connector, db);
|
||||
/* HDMI Forum Vendor-Specific Data Block */
|
||||
else if (cea_db_is_hdmi_hf_vsdb(db))
|
||||
parse_hdmi_hf_vsdb(connector, db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block
|
||||
* @connector: connector corresponding to the HDMI sink
|
||||
* @db: start of the CEA vendor specific block
|
||||
*
|
||||
* Parses the HDMI VCDB to extract sink info for @connector.
|
||||
*/
|
||||
static void
|
||||
drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db)
|
||||
{
|
||||
/*
|
||||
* Check if the sink specifies underscan
|
||||
* support for:
|
||||
* BIT 5: preferred video format
|
||||
* BIT 3: IT video format
|
||||
* BIT 1: CE video format
|
||||
*/
|
||||
|
||||
connector->pt_scan_info =
|
||||
(db[2] & (BIT(4) | BIT(5))) >> 4;
|
||||
connector->it_scan_info =
|
||||
(db[2] & (BIT(3) | BIT(2))) >> 2;
|
||||
connector->ce_scan_info =
|
||||
db[2] & (BIT(1) | BIT(0));
|
||||
|
||||
DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)",
|
||||
(int) connector->pt_scan_info,
|
||||
(int) connector->it_scan_info,
|
||||
(int) connector->ce_scan_info);
|
||||
}
|
||||
|
||||
static bool drm_edid_is_luminance_value_present(
|
||||
u32 block_length, enum luminance_value value)
|
||||
{
|
||||
return block_length > NO_LUMINANCE_DATA && value <= block_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_extract_hdr_db - Parse the HDMI HDR extended block
|
||||
* @connector: connector corresponding to the HDMI sink
|
||||
* @db: start of the HDMI HDR extended block
|
||||
*
|
||||
* Parses the HDMI HDR extended block to extract sink info for @connector.
|
||||
*/
|
||||
static void
|
||||
drm_extract_hdr_db(struct drm_connector *connector, const u8 *db)
|
||||
{
|
||||
|
||||
u8 len = 0;
|
||||
|
||||
if (!db) {
|
||||
DRM_ERROR("invalid db\n");
|
||||
return;
|
||||
}
|
||||
|
||||
len = db[0] & 0x1f;
|
||||
/* Byte 3: Electro-Optical Transfer Functions */
|
||||
connector->hdr_eotf = db[2] & 0x3F;
|
||||
|
||||
/* Byte 4: Static Metadata Descriptor Type 1 */
|
||||
connector->hdr_metadata_type_one = (db[3] & BIT(0));
|
||||
|
||||
/* Byte 5: Desired Content Maximum Luminance */
|
||||
if (drm_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE))
|
||||
connector->hdr_max_luminance =
|
||||
db[MAXIMUM_LUMINANCE];
|
||||
|
||||
/* Byte 6: Desired Content Max Frame-average Luminance */
|
||||
if (drm_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE))
|
||||
connector->hdr_avg_luminance =
|
||||
db[FRAME_AVERAGE_LUMINANCE];
|
||||
|
||||
/* Byte 7: Desired Content Min Luminance */
|
||||
if (drm_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE))
|
||||
connector->hdr_min_luminance =
|
||||
db[MINIMUM_LUMINANCE];
|
||||
|
||||
connector->hdr_supported = true;
|
||||
|
||||
DRM_DEBUG_KMS("HDR electro-optical %d\n", connector->hdr_eotf);
|
||||
DRM_DEBUG_KMS("metadata desc 1 %d\n", connector->hdr_metadata_type_one);
|
||||
DRM_DEBUG_KMS("max luminance %d\n", connector->hdr_max_luminance);
|
||||
DRM_DEBUG_KMS("avg luminance %d\n", connector->hdr_avg_luminance);
|
||||
DRM_DEBUG_KMS("min luminance %d\n", connector->hdr_min_luminance);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks
|
||||
* @connector: connector corresponding to the HDMI sink
|
||||
* @edid: handle to the EDID structure
|
||||
* Parses the all extended tag blocks extract sink info for @connector.
|
||||
*/
|
||||
static void
|
||||
drm_hdmi_extract_extended_blk_info(struct drm_connector *connector,
|
||||
struct edid *edid)
|
||||
{
|
||||
const u8 *cea = drm_find_cea_extension(edid);
|
||||
const u8 *db = NULL;
|
||||
|
||||
if (cea && cea_revision(cea) >= 3) {
|
||||
int i, start, end;
|
||||
|
||||
if (cea_db_offsets(cea, &start, &end))
|
||||
return;
|
||||
|
||||
for_each_cea_db(cea, i, start, end) {
|
||||
db = &cea[i];
|
||||
|
||||
if (cea_db_tag(db) == EXTENDED_TAG) {
|
||||
DRM_DEBUG_KMS("found extended tag block = %d\n",
|
||||
db[1]);
|
||||
switch (db[1]) {
|
||||
case VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK:
|
||||
drm_extract_vcdb_info(connector, db);
|
||||
break;
|
||||
case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK:
|
||||
drm_extract_hdr_db(connector, db);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u8 *
|
||||
drm_edid_find_extended_tag_block(struct edid *edid, int blk_id)
|
||||
{
|
||||
u8 *db = NULL;
|
||||
u8 *cea = NULL;
|
||||
|
||||
if (!edid) {
|
||||
pr_err("%s: invalid input\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cea = drm_find_cea_extension(edid);
|
||||
|
||||
if (cea && cea_revision(cea) >= 3) {
|
||||
int i, start, end;
|
||||
|
||||
if (cea_db_offsets(cea, &start, &end))
|
||||
return NULL;
|
||||
|
||||
for_each_cea_db(cea, i, start, end) {
|
||||
db = &cea[i];
|
||||
if ((cea_db_tag(db) == EXTENDED_TAG) &&
|
||||
(db[1] == blk_id))
|
||||
return db;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_YCbCr420VDB_modes - add the modes found in Ycbcr420 VDB block
|
||||
* @connector: connector corresponding to the HDMI sink
|
||||
* @edid: handle to the EDID structure
|
||||
* Parses the YCbCr420 VDB block and adds the modes to @connector.
|
||||
*/
|
||||
static int
|
||||
add_YCbCr420VDB_modes(struct drm_connector *connector, struct edid *edid)
|
||||
{
|
||||
|
||||
const u8 *db = NULL;
|
||||
u32 i = 0;
|
||||
u32 modes = 0;
|
||||
u32 video_format = 0;
|
||||
u8 len = 0;
|
||||
|
||||
/*Find the YCbCr420 VDB*/
|
||||
db = drm_edid_find_extended_tag_block(edid, Y420_VIDEO_DATA_BLOCK);
|
||||
/* Offset to byte 3 */
|
||||
if (db) {
|
||||
len = db[0] & 0x1F;
|
||||
db += 2;
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
video_format = *(db + i) & 0x7F;
|
||||
mode = drm_display_mode_from_vic_index(connector,
|
||||
db, len-1, i);
|
||||
if (mode) {
|
||||
DRM_DEBUG_KMS("Adding mode for vic = %d\n",
|
||||
video_format);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
modes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
static void
|
||||
monitor_name(struct detailed_timing *t, void *data)
|
||||
{
|
||||
|
@ -3997,6 +4229,10 @@ static void drm_add_display_info(struct edid *edid,
|
|||
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
|
||||
}
|
||||
|
||||
/* Extract audio and video latency fields for the sink */
|
||||
drm_hdmi_extract_vsdbs_info(connector, edid);
|
||||
/* Extract info from extended tag blocks */
|
||||
drm_hdmi_extract_extended_blk_info(connector, edid);
|
||||
/* HDMI deep color modes supported? Assign to info, if so */
|
||||
drm_assign_hdmi_deep_color_info(edid, info, connector);
|
||||
|
||||
|
@ -4227,6 +4463,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
|
|||
num_modes += add_cea_modes(connector, edid);
|
||||
num_modes += add_alternate_cea_modes(connector, edid);
|
||||
num_modes += add_displayid_detailed_modes(connector, edid);
|
||||
num_modes += add_YCbCr420VDB_modes(connector, edid);
|
||||
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
|
||||
num_modes += add_inferred_modes(connector, edid);
|
||||
|
||||
|
|
|
@ -649,6 +649,15 @@ struct drm_encoder {
|
|||
* @rr_capable: when set the sink is capable of initiating an SCDC read request
|
||||
* @supports_scramble: when set the sink supports less than 340Mcsc scrambling
|
||||
* @flags_3d: 3D view(s) supported by the sink, see drm_edid.h (DRM_EDID_3D_*)
|
||||
* @pt_scan_info: PT scan info obtained from the VCDB of EDID
|
||||
* @it_scan_info: IT scan info obtained from the VCDB of EDID
|
||||
* @ce_scan_info: CE scan info obtained from the VCDB of EDID
|
||||
* @hdr_eotf: Electro optical transfer function obtained from HDR block
|
||||
* @hdr_metadata_type_one: Metadata type one obtained from HDR block
|
||||
* @hdr_max_luminance: desired max luminance obtained from HDR block
|
||||
* @hdr_avg_luminance: desired avg luminance obtained from HDR block
|
||||
* @hdr_min_luminance: desired min luminance obtained from HDR block
|
||||
* @hdr_supported: does the sink support HDR content
|
||||
* @edid_corrupt: indicates whether the last read EDID was corrupt
|
||||
* @debugfs_entry: debugfs directory for this connector
|
||||
* @state: current atomic state for this connector
|
||||
|
@ -728,7 +737,15 @@ struct drm_connector {
|
|||
bool rr_capable;
|
||||
bool supports_scramble;
|
||||
int flags_3d;
|
||||
|
||||
u8 pt_scan_info;
|
||||
u8 it_scan_info;
|
||||
u8 ce_scan_info;
|
||||
u32 hdr_eotf;
|
||||
bool hdr_metadata_type_one;
|
||||
u32 hdr_max_luminance;
|
||||
u32 hdr_avg_luminance;
|
||||
u32 hdr_min_luminance;
|
||||
bool hdr_supported;
|
||||
/* Flag for raw EDID header corruption - used in Displayport
|
||||
* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
|
||||
*/
|
||||
|
|
|
@ -73,7 +73,8 @@
|
|||
#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14)
|
||||
#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14)
|
||||
#define DRM_MODE_FLAG_SEAMLESS (1<<19)
|
||||
|
||||
#define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20)
|
||||
#define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21)
|
||||
|
||||
/* DPMS flags */
|
||||
/* bit compatible with the xorg definitions. */
|
||||
|
|
Loading…
Add table
Reference in a new issue