Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4103 → Rev 4104

/drivers/video/drm/drm_edid.c
125,6 → 125,9
 
/* ViewSonic VA2026w */
{ "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
 
/* Medion MD 30217 PG */
{ "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 },
};
 
/*
931,6 → 934,36
.vrefresh = 100, },
};
 
/*
* HDMI 1.4 4k modes.
*/
static const struct drm_display_mode edid_4k_modes[] = {
/* 1 - 3840x2160@30Hz */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
3840, 4016, 4104, 4400, 0,
2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 30, },
/* 2 - 3840x2160@25Hz */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
3840, 4896, 4984, 5280, 0,
2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 25, },
/* 3 - 3840x2160@24Hz */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
3840, 5116, 5204, 5500, 0,
2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 24, },
/* 4 - 4096x2160@24Hz (SMPTE) */
{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000,
4096, 5116, 5204, 5500, 0,
2160, 2168, 2178, 2250, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 24, },
};
 
/*** DDC fetch and block validation ***/
 
static const u8 edid_header[] = {
2287,7 → 2320,6
return closure.modes;
}
 
#define HDMI_IDENTIFIER 0x000C03
#define AUDIO_BLOCK 0x01
#define VIDEO_BLOCK 0x02
#define VENDOR_BLOCK 0x03
2298,10 → 2330,10
#define EDID_CEA_YCRCB422 (1 << 4)
#define EDID_CEA_VCDB_QS (1 << 6)
 
/**
/*
* Search EDID for CEA extension block.
*/
u8 *drm_find_cea_extension(struct edid *edid)
static u8 *drm_find_cea_extension(struct edid *edid)
{
u8 *edid_ext = NULL;
int i;
2322,7 → 2354,6
 
return edid_ext;
}
EXPORT_SYMBOL(drm_find_cea_extension);
 
/*
* Calculate the alternate clock for the CEA mode
2380,6 → 2411,54
}
EXPORT_SYMBOL(drm_match_cea_mode);
 
/*
* Calculate the alternate clock for HDMI modes (those from the HDMI vendor
* specific block).
*
* It's almost like cea_mode_alternate_clock(), we just need to add an
* exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this
* one.
*/
static unsigned int
hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
{
if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160)
return hdmi_mode->clock;
 
return cea_mode_alternate_clock(hdmi_mode);
}
 
/*
* drm_match_hdmi_mode - look for a HDMI mode matching given mode
* @to_match: display mode
*
* An HDMI mode is one defined in the HDMI vendor specific block.
*
* Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
*/
static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
{
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 ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
drm_mode_equal_no_clocks(to_match, hdmi_mode))
return mode + 1;
}
return 0;
}
 
static int
add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
{
2397,18 → 2476,26
* with the alternate clock for certain CEA modes.
*/
list_for_each_entry(mode, &connector->probed_modes, head) {
const struct drm_display_mode *cea_mode;
const struct drm_display_mode *cea_mode = NULL;
struct drm_display_mode *newmode;
u8 cea_mode_idx = drm_match_cea_mode(mode) - 1;
u8 mode_idx = drm_match_cea_mode(mode) - 1;
unsigned int clock1, clock2;
 
if (cea_mode_idx >= ARRAY_SIZE(edid_cea_modes))
if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
cea_mode = &edid_cea_modes[mode_idx];
clock2 = cea_mode_alternate_clock(cea_mode);
} else {
mode_idx = drm_match_hdmi_mode(mode) - 1;
if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
cea_mode = &edid_4k_modes[mode_idx];
clock2 = hdmi_mode_alternate_clock(cea_mode);
}
}
 
if (!cea_mode)
continue;
 
cea_mode = &edid_cea_modes[cea_mode_idx];
 
clock1 = cea_mode->clock;
clock2 = cea_mode_alternate_clock(cea_mode);
 
if (clock1 == clock2)
continue;
2442,10 → 2529,11
}
 
static int
do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
{
struct drm_device *dev = connector->dev;
u8 * mode, cea_mode;
const u8 *mode;
u8 cea_mode;
int modes = 0;
 
for (mode = db; mode < db + len; mode++) {
2465,7 → 2553,69
return modes;
}
 
/*
* do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
* @connector: connector corresponding to the HDMI sink
* @db: start of the CEA vendor specific block
* @len: length of the CEA block payload, ie. one can access up to db[len]
*
* Parses the HDMI VSDB looking for modes to add to @connector.
*/
static int
do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
{
struct drm_device *dev = connector->dev;
int modes = 0, offset = 0, i;
u8 vic_len;
 
if (len < 8)
goto out;
 
/* no HDMI_Video_Present */
if (!(db[8] & (1 << 5)))
goto out;
 
/* Latency_Fields_Present */
if (db[8] & (1 << 7))
offset += 2;
 
/* I_Latency_Fields_Present */
if (db[8] & (1 << 6))
offset += 2;
 
/* the declared length is not long enough for the 2 first bytes
* of additional video format capabilities */
offset += 2;
if (len < (8 + offset))
goto out;
 
vic_len = db[8 + offset] >> 5;
 
for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
struct drm_display_mode *newmode;
u8 vic;
 
vic = db[9 + offset + i];
 
vic--; /* VICs start at 1 */
if (vic >= ARRAY_SIZE(edid_4k_modes)) {
DRM_ERROR("Unknown HDMI VIC: %d\n", vic);
continue;
}
 
newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
if (!newmode)
continue;
 
drm_mode_probed_add(connector, newmode);
modes++;
}
 
out:
return modes;
}
 
static int
cea_db_payload_len(const u8 *db)
{
return db[0] & 0x1f;
2496,6 → 2646,21
return 0;
}
 
static bool cea_db_is_hdmi_vsdb(const u8 *db)
{
int hdmi_id;
 
if (cea_db_tag(db) != VENDOR_BLOCK)
return false;
 
if (cea_db_payload_len(db) < 5)
return false;
 
hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
 
return hdmi_id == HDMI_IEEE_OUI;
}
 
#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)
 
2502,8 → 2667,9
static int
add_cea_modes(struct drm_connector *connector, struct edid *edid)
{
u8 * cea = drm_find_cea_extension(edid);
u8 * db, dbl;
const u8 *cea = drm_find_cea_extension(edid);
const u8 *db;
u8 dbl;
int modes = 0;
 
if (cea && cea_revision(cea) >= 3) {
2518,6 → 2684,8
 
if (cea_db_tag(db) == VIDEO_BLOCK)
modes += do_cea_modes (connector, db+1, dbl);
else if (cea_db_is_hdmi_vsdb(db))
modes += do_hdmi_vsdb_modes(connector, db, dbl);
}
}
 
2570,21 → 2738,6
*(u8 **)data = t->data.other_data.data.str.str;
}
 
static bool cea_db_is_hdmi_vsdb(const u8 *db)
{
int hdmi_id;
 
if (cea_db_tag(db) != VENDOR_BLOCK)
return false;
 
if (cea_db_payload_len(db) < 5)
return false;
 
hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
 
return hdmi_id == HDMI_IDENTIFIER;
}
 
/**
* drm_edid_to_eld - build ELD from EDID
* @connector: connector corresponding to the HDMI/DP sink
2732,6 → 2885,60
EXPORT_SYMBOL(drm_edid_to_sad);
 
/**
* drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
* @edid: EDID to parse
* @sadb: pointer to the speaker block
*
* Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
* Note: returned pointer needs to be kfreed
*
* Return number of found Speaker Allocation Blocks or negative number on error.
*/
int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
{
int count = 0;
int i, start, end, dbl;
const u8 *cea;
 
cea = drm_find_cea_extension(edid);
if (!cea) {
DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
return -ENOENT;
}
 
if (cea_revision(cea) < 3) {
DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
return -ENOTSUPP;
}
 
if (cea_db_offsets(cea, &start, &end)) {
DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
return -EPROTO;
}
 
for_each_cea_db(cea, i, start, end) {
const u8 *db = &cea[i];
 
if (cea_db_tag(db) == SPEAKER_BLOCK) {
dbl = cea_db_payload_len(db);
 
/* Speaker Allocation Data Block */
if (dbl == 3) {
*sadb = kmalloc(dbl, GFP_KERNEL);
if (!*sadb)
return -ENOMEM;
memcpy(*sadb, &db[1], dbl);
count = dbl;
break;
}
}
}
 
return count;
}
EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
 
/**
* drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
* @connector: connector associated with the HDMI/DP sink
* @mode: the display mode
3102,9 → 3309,10
if (err < 0)
return err;
 
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
frame->pixel_repeat = 1;
 
frame->video_code = drm_match_cea_mode(mode);
if (!frame->video_code)
return 0;
 
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
3112,3 → 3320,39
return 0;
}
EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
 
/**
* drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
* data from a DRM display mode
* @frame: HDMI vendor infoframe
* @mode: DRM display mode
*
* Note that there's is a need to send HDMI vendor infoframes only when using a
* 4k or stereoscopic 3D mode. So when giving any other mode as input this
* function will return -EINVAL, error that can be safely ignored.
*
* Returns 0 on success or a negative error code on failure.
*/
int
drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
const struct drm_display_mode *mode)
{
int err;
u8 vic;
 
if (!frame || !mode)
return -EINVAL;
 
vic = drm_match_hdmi_mode(mode);
if (!vic)
return -EINVAL;
 
err = hdmi_vendor_infoframe_init(frame);
if (err < 0)
return err;
 
frame->vic = vic;
 
return 0;
}
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);