#define EDID_QUIRK_FORCE_8BPC (1 << 8)
/* Force 12bpc */
#define EDID_QUIRK_FORCE_12BPC (1 << 9)
+/* Force 6bpc */
+#define EDID_QUIRK_FORCE_6BPC (1 << 10)
struct detailed_mode_closure {
struct drm_connector *connector;
/* Unknown Acer */
{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+ /* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
+ { "AEO", 0, EDID_QUIRK_FORCE_6BPC },
+
/* Belinea 10 15 55 */
{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 24, },
+ /* 5 - 3840x2160@60Hz */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000,
+ 3840, 4016, 4104, 4400, 0,
+ 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
};
/*** DDC fetch and block validation ***/
return clock;
}
+static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
+ unsigned int clock_tolerance)
+{
+ u8 mode;
+
+ if (!to_match->clock)
+ return 0;
+
+ for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) {
+ const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
+ unsigned int clock1, clock2;
+
+ /* Check both 60Hz and 59.94Hz */
+ clock1 = cea_mode->clock;
+ clock2 = cea_mode_alternate_clock(cea_mode);
+
+ if (abs(to_match->clock - clock1) > clock_tolerance &&
+ abs(to_match->clock - clock2) > clock_tolerance)
+ continue;
+
+ if (drm_mode_equal_no_clocks(to_match, cea_mode))
+ return mode + 1;
+ }
+
+ return 0;
+}
+
/**
* drm_match_cea_mode - look for a CEA mode matching given mode
* @to_match: display mode
return cea_mode_alternate_clock(hdmi_mode);
}
+static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
+ unsigned int clock_tolerance)
+{
+ u8 mode;
+
+ if (!to_match->clock)
+ return 0;
+
+ for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) {
+ const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode];
+ unsigned int clock1, clock2;
+
+ /* Make sure to also match alternate clocks */
+ clock1 = hdmi_mode->clock;
+ clock2 = hdmi_mode_alternate_clock(hdmi_mode);
+
+ if (abs(to_match->clock - clock1) > clock_tolerance &&
+ abs(to_match->clock - clock2) > clock_tolerance)
+ continue;
+
+ if (drm_mode_equal_no_clocks(to_match, hdmi_mode))
+ return mode + 1;
+ }
+
+ return 0;
+}
+
/*
* drm_match_hdmi_mode - look for a HDMI mode matching given mode
* @to_match: display mode
return hdmi_id == HDMI_IEEE_OUI;
}
+static bool cea_db_is_hdmi_hf_vsdb(const u8 *db)
+{
+ int hdmi_id;
+
+ if (cea_db_tag(db) != VENDOR_BLOCK)
+ return false;
+
+ if (cea_db_payload_len(db) < 7)
+ return false;
+
+ hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
+
+ return hdmi_id == HDMI_IEEE_OUI_HF;
+}
+
#define for_each_cea_db(cea, i, start, end) \
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
u8 mode_idx;
const char *type;
- mode_idx = drm_match_cea_mode(mode) - 1;
+ /*
+ * allow 5kHz clock difference either way to account for
+ * the 10kHz clock resolution limit of detailed timings.
+ */
+ mode_idx = drm_match_cea_mode_clock_tolerance(mode, 5) - 1;
if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
type = "CEA";
cea_mode = &edid_cea_modes[mode_idx];
clock1 = cea_mode->clock;
clock2 = cea_mode_alternate_clock(cea_mode);
} else {
- mode_idx = drm_match_hdmi_mode(mode) - 1;
+ mode_idx = drm_match_hdmi_mode_clock_tolerance(mode, 5) - 1;
if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
type = "HDMI";
cea_mode = &edid_4k_modes[mode_idx];
connector->audio_latency[1]);
}
+static void
+parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
+{
+ u8 len = cea_db_payload_len(db);
+
+ if (len < 7)
+ return;
+
+ if (db[4] != 1)
+ return; /* invalid version */
+
+ connector->max_tmds_char = db[5] * 5;
+ connector->scdc_present = db[6] & (1 << 7);
+ connector->rr_capable = db[6] & (1 << 6);
+ connector->flags_3d = db[6] & 0x7;
+ connector->lte_340mcsc_scramble = db[6] & (1 << 3);
+
+ DRM_DEBUG_KMS("HDMI v2: max TMDS clock %d, "
+ "scdc %s, "
+ "rr %s, "
+ "3D flags 0x%x, "
+ "scramble %s\n",
+ connector->max_tmds_char,
+ connector->scdc_present ? "available" : "not available",
+ connector->rr_capable ? "capable" : "not capable",
+ connector->flags_3d,
+ connector->lte_340mcsc_scramble ?
+ "supported" : "not supported");
+}
+
static void
monitor_name(struct detailed_timing *t, void *data)
{
/* 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);
break;
default:
break;
drm_add_display_info(edid, &connector->display_info, connector);
+ if (quirks & EDID_QUIRK_FORCE_6BPC)
+ connector->display_info.bpc = 6;
+
if (quirks & EDID_QUIRK_FORCE_8BPC)
connector->display_info.bpc = 8;