|
|
/* See LICENSE file for copyright and license details. */
#include "common.h"
/*
* EDID structure revision 1.1:
* http://en.wikipedia.org/w/index.php?title=Extended_display_identification_data&oldid=46295569
*
* EDID structure revision 1.3:
* https://en.wikipedia.org/w/index.php?title=Extended_Display_Identification_Data&oldid=805561402#EDID_1.3_data_format
*
* EDID structure revision 1.4:
* http://en.wikipedia.org/wiki/Extended_display_identification_data
*/
/**
* Parse the EDID of a monitor
*
* @param this Instance of a data structure to fill with the information about the EDID;
* it must contain the EDID and its length
* @param fields OR:ed identifiers for the information about the EDID that should be parsed;
* fields that do not have to do with EDID are ignored
* @return Non-zero on error
*/
int
libgamma_internal_parse_edid(struct libgamma_crtc_information *restrict this, unsigned long long fields)
{
#define M(value)\
(this->edid[index++] != (value))
uint16_t red_x, green_x, blue_x, white_x;
uint16_t red_y, green_y, blue_y, white_y;
int error = 0, checksum = 0;
size_t i, index = 0;
/* If the length of the EDID is less than 128 bytes, we know that it is
* not of EDID structure revision 1.0–1.4, and thus we do not support it;
* however, extensions allows it to be longer tha 128 bytes */
if (this->edid_length < 128)
error = LIBGAMMA_EDID_LENGTH_UNSUPPORTED;
/* Check that the magic number of that of the EDID structure */
else if (M(0x00) || M(0xFF) || M(0xFF) || M(0xFF) || M(0xFF) || M(0xFF) || M(0xFF) || M(0x00))
error = LIBGAMMA_EDID_WRONG_MAGIC_NUMBER;
/* Check that EDID structure revision 1.x is used. We only know that we
* support revisions 1.1, 1.3, and 1.4, and since they are all comptible,
* we assume that all 1.x are compatible. */
else if (this->edid[18] != 1)
error = LIBGAMMA_EDID_REVISION_UNSUPPORTED;
/* If we have encountered an error, report it for the fields that require
* the EDID to be parsed. Note that it is not stored for the EDID field
* itself because it is not considered an error just because we do not
* support the used version. */
this->width_mm_edid_error = this->height_mm_edid_error = this->gamma_error = error;
this->chroma_error = this->white_point_error = error;
/* Set errors and exit if we have error, especially important if this->edid_length < 128 */
if (error)
goto out;
/* Retrieve the size of the viewport. This is done even if it is not
* requested because it is not worth it to branch. */
this->width_mm_edid = (size_t)this->edid[21] * 10;
this->height_mm_edid = (size_t)this->edid[22] * 10;
/* Retrieve the monitor's gamma characteristics */
if (fields & LIBGAMMA_CRTC_INFO_GAMMA) {
if (this->edid[23] == 0xFF) {
/* If the gamma charactistics is FFh (3,55) it should be interpreted as not specified. */
this->gamma_error = LIBGAMMA_GAMMA_NOT_SPECIFIED;
} else {
this->gamma_blue = (float)((int)this->edid[23] + 100) / 100.f;
this->gamma_red = this->gamma_green = this->gamma_blue;
}
}
/* Retrieve the monitor's subpixel chromas */
if (fields & (LIBGAMMA_CRTC_INFO_CHROMA | LIBGAMMA_CRTC_INFO_WHITE_POINT)) {
red_x = (uint16_t)((((uint16_t)this->edid[25] >> 6) & 2) | ((uint16_t)this->edid[27] << 2));
red_y = (uint16_t)((((uint16_t)this->edid[25] >> 4) & 2) | ((uint16_t)this->edid[28] << 2));
green_x = (uint16_t)((((uint16_t)this->edid[25] >> 2) & 2) | ((uint16_t)this->edid[29] << 2));
green_y = (uint16_t)((((uint16_t)this->edid[25] >> 0) & 2) | ((uint16_t)this->edid[30] << 2));
blue_x = (uint16_t)((((uint16_t)this->edid[26] >> 6) & 2) | ((uint16_t)this->edid[31] << 2));
blue_y = (uint16_t)((((uint16_t)this->edid[26] >> 4) & 2) | ((uint16_t)this->edid[32] << 2));
white_x = (uint16_t)((((uint16_t)this->edid[26] >> 2) & 2) | ((uint16_t)this->edid[33] << 2));
white_y = (uint16_t)((((uint16_t)this->edid[26] >> 6) & 2) | ((uint16_t)this->edid[34] << 2));
/* Even though the maximum value as encoded is 1023, the values should be divided by 1024 */
this->red_chroma_x = (float)red_x / 1024.f;
this->red_chroma_y = (float)red_y / 1024.f;
this->green_chroma_x = (float)green_x / 1024.f;
this->green_chroma_y = (float)green_y / 1024.f;
this->blue_chroma_x = (float)blue_x / 1024.f;
this->blue_chroma_y = (float)blue_y / 1024.f;
this->white_point_x = (float)white_x / 1024.f;
this->white_point_y = (float)white_y / 1024.f;
}
/* Calculate and test the checksum. It is not considered
* an error that the gamma characteristics is left
* unspecified in the EDID. */
if (!error)
for (i = 0; i < 128; i++)
checksum += (int)this->edid[i];
/* The checksum should be zero */
if (checksum & 255) {
/* Store the error in all fields that require the EDID to be parsed,
* as well as the EDID field itself */
error = LIBGAMMA_EDID_CHECKSUM_ERROR;
out:
this->edid_error = this->width_mm_edid_error = this->height_mm_edid_error = error;
/* If the gamma characteristics is not specified, that is kept,
* and the checksum error is augmented. */
this->gamma_error = this->gamma_error == LIBGAMMA_GAMMA_NOT_SPECIFIED
? LIBGAMMA_GAMMA_NOT_SPECIFIED_AND_EDID_CHECKSUM_ERROR : error;
}
/* Return whether or not we encountered an error or if
* the gamma characteristics was requested but is not
* specified in the monitor's EDID */
return error | this->gamma_error;
#undef M
}
|