34,6 → 34,7 |
#include <linux/module.h> |
#include <drm/drmP.h> |
#include <drm/drm_edid.h> |
#include <drm/drm_displayid.h> |
|
#define version_greater(edid, maj, min) \ |
(((edid)->version > (maj)) || \ |
632,27 → 633,27 |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | |
DRM_MODE_FLAG_INTERLACE), |
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 6 - 1440x480i@60Hz */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 6 - 720(1440)x480i@60Hz */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 7 - 1440x480i@60Hz */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 7 - 720(1440)x480i@60Hz */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 8 - 1440x240@60Hz */ |
{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, |
1602, 1716, 0, 240, 244, 247, 262, 0, |
/* 8 - 720(1440)x240@60Hz */ |
{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, |
801, 858, 0, 240, 244, 247, 262, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 9 - 1440x240@60Hz */ |
{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, |
1602, 1716, 0, 240, 244, 247, 262, 0, |
/* 9 - 720(1440)x240@60Hz */ |
{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, |
801, 858, 0, 240, 244, 247, 262, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
714,27 → 715,27 |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | |
DRM_MODE_FLAG_INTERLACE), |
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 21 - 1440x576i@50Hz */ |
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 21 - 720(1440)x576i@50Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 22 - 1440x576i@50Hz */ |
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 22 - 720(1440)x576i@50Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 23 - 1440x288@50Hz */ |
{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, |
1590, 1728, 0, 288, 290, 293, 312, 0, |
/* 23 - 720(1440)x288@50Hz */ |
{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, |
795, 864, 0, 288, 290, 293, 312, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 24 - 1440x288@50Hz */ |
{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, |
1590, 1728, 0, 288, 290, 293, 312, 0, |
/* 24 - 720(1440)x288@50Hz */ |
{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, |
795, 864, 0, 288, 290, 293, 312, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
837,17 → 838,17 |
796, 864, 0, 576, 581, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), |
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 44 - 1440x576i@100Hz */ |
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 44 - 720(1440)x576i@100Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 45 - 1440x576i@100Hz */ |
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 45 - 720(1440)x576i@100Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_DBLCLK), |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 46 - 1920x1080i@120Hz */ |
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, |
870,15 → 871,15 |
798, 858, 0, 480, 489, 495, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), |
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 50 - 1440x480i@120Hz */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 50 - 720(1440)x480i@120Hz */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 51 - 1440x480i@120Hz */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 51 - 720(1440)x480i@120Hz */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
892,15 → 893,15 |
796, 864, 0, 576, 581, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), |
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 54 - 1440x576i@200Hz */ |
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 54 - 720(1440)x576i@200Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 55 - 1440x576i@200Hz */ |
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, |
1590, 1728, 0, 576, 580, 586, 625, 0, |
/* 55 - 720(1440)x576i@200Hz */ |
{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, |
795, 864, 0, 576, 580, 586, 625, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
914,15 → 915,15 |
798, 858, 0, 480, 489, 495, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), |
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
/* 58 - 1440x480i@240 */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 58 - 720(1440)x480i@240 */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, |
/* 59 - 1440x480i@240 */ |
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, |
1602, 1716, 0, 480, 488, 494, 525, 0, |
/* 59 - 720(1440)x480i@240 */ |
{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, |
801, 858, 0, 480, 488, 494, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), |
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, |
1014,6 → 1015,27 |
MODULE_PARM_DESC(edid_fixup, |
"Minimum number of valid EDID header bytes (0-8, default 6)"); |
|
static void drm_get_displayid(struct drm_connector *connector, |
struct edid *edid); |
|
static int drm_edid_block_checksum(const u8 *raw_edid) |
{ |
int i; |
u8 csum = 0; |
for (i = 0; i < EDID_LENGTH; i++) |
csum += raw_edid[i]; |
|
return csum; |
} |
|
static bool drm_edid_is_zero(const u8 *in_edid, int length) |
{ |
if (memchr_inv(in_edid, 0, length)) |
return false; |
|
return true; |
} |
|
/** |
* drm_edid_block_valid - Sanity check the EDID block (base or extension) |
* @raw_edid: pointer to raw EDID block |
1027,8 → 1049,7 |
*/ |
bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) |
{ |
int i; |
u8 csum = 0; |
u8 csum; |
struct edid *edid = (struct edid *)raw_edid; |
|
if (WARN_ON(!raw_edid)) |
1048,8 → 1069,7 |
} |
} |
|
for (i = 0; i < EDID_LENGTH; i++) |
csum += raw_edid[i]; |
csum = drm_edid_block_checksum(raw_edid); |
if (csum) { |
if (print_bad_edid) { |
DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); |
1080,10 → 1100,14 |
|
bad: |
if (print_bad_edid) { |
if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) { |
printk(KERN_ERR "EDID block is all zeroes\n"); |
} else { |
printk(KERN_ERR "Raw EDID:\n"); |
print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1, |
raw_edid, EDID_LENGTH, false); |
} |
} |
return false; |
} |
EXPORT_SYMBOL(drm_edid_block_valid); |
1115,7 → 1139,7 |
#define DDC_SEGMENT_ADDR 0x30 |
/** |
* drm_do_probe_ddc_edid() - get EDID information via I2C |
* @adapter: I2C device adaptor |
* @data: I2C device adapter |
* @buf: EDID data buffer to be filled |
* @block: 128 byte EDID block to start fetching from |
* @len: EDID data buffer length to fetch |
1125,9 → 1149,9 |
* Return: 0 on success or -1 on failure. |
*/ |
static int |
drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, |
int block, int len) |
drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) |
{ |
struct i2c_adapter *adapter = data; |
unsigned char start = block * EDID_LENGTH; |
unsigned char segment = block >> 1; |
unsigned char xfers = segment ? 3 : 2; |
1176,17 → 1200,27 |
return ret == xfers ? 0 : -1; |
} |
|
static bool drm_edid_is_zero(u8 *in_edid, int length) |
/** |
* drm_do_get_edid - get EDID data using a custom EDID block read function |
* @connector: connector we're probing |
* @get_edid_block: EDID block read function |
* @data: private data passed to the block read function |
* |
* When the I2C adapter connected to the DDC bus is hidden behind a device that |
* exposes a different interface to read EDID blocks this function can be used |
* to get EDID data using a custom block read function. |
* |
* As in the general case the DDC bus is accessible by the kernel at the I2C |
* level, drivers must make all reasonable efforts to expose it as an I2C |
* adapter and use drm_get_edid() instead of abusing this function. |
* |
* Return: Pointer to valid EDID or NULL if we couldn't find any. |
*/ |
struct edid *drm_do_get_edid(struct drm_connector *connector, |
int (*get_edid_block)(void *data, u8 *buf, unsigned int block, |
size_t len), |
void *data) |
{ |
if (memchr_inv(in_edid, 0, length)) |
return false; |
|
return true; |
} |
|
static u8 * |
drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) |
{ |
int i, j = 0, valid_extensions = 0; |
u8 *block, *new; |
bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); |
1196,7 → 1230,7 |
|
/* base block fetch */ |
for (i = 0; i < 4; i++) { |
if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) |
if (get_edid_block(data, block, 0, EDID_LENGTH)) |
goto out; |
if (drm_edid_block_valid(block, 0, print_bad_edid)) |
break; |
1210,7 → 1244,7 |
|
/* if there's no extensions, we're done */ |
if (block[0x7e] == 0) |
return block; |
return (struct edid *)block; |
|
new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); |
if (!new) |
1219,7 → 1253,7 |
|
for (j = 1; j <= block[0x7e]; j++) { |
for (i = 0; i < 4; i++) { |
if (drm_do_probe_ddc_edid(adapter, |
if (get_edid_block(data, |
block + (valid_extensions + 1) * EDID_LENGTH, |
j, EDID_LENGTH)) |
goto out; |
1247,7 → 1281,7 |
block = new; |
} |
|
return block; |
return (struct edid *)block; |
|
carp: |
if (print_bad_edid) { |
1260,6 → 1294,7 |
kfree(block); |
return NULL; |
} |
EXPORT_SYMBOL_GPL(drm_do_get_edid); |
|
/** |
* drm_probe_ddc() - probe DDC presence |
1289,11 → 1324,14 |
struct edid *drm_get_edid(struct drm_connector *connector, |
struct i2c_adapter *adapter) |
{ |
struct edid *edid = NULL; |
struct edid *edid; |
|
if (drm_probe_ddc(adapter)) |
edid = (struct edid *)drm_do_get_edid(connector, adapter); |
if (!drm_probe_ddc(adapter)) |
return NULL; |
|
edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); |
if (edid) |
drm_get_displayid(connector, edid); |
return edid; |
} |
EXPORT_SYMBOL(drm_get_edid); |
2103,7 → 2141,8 |
add_inferred_modes(struct drm_connector *connector, struct edid *edid) |
{ |
struct detailed_mode_closure closure = { |
connector, edid, 0, 0, 0 |
.connector = connector, |
.edid = edid, |
}; |
|
if (version_greater(edid, 1, 0)) |
2169,7 → 2208,8 |
((edid->established_timings.mfg_rsvd & 0x80) << 9); |
int i, modes = 0; |
struct detailed_mode_closure closure = { |
connector, edid, 0, 0, 0 |
.connector = connector, |
.edid = edid, |
}; |
|
for (i = 0; i <= EDID_EST_TIMINGS; i++) { |
2227,7 → 2267,8 |
{ |
int i, modes = 0; |
struct detailed_mode_closure closure = { |
connector, edid, 0, 0, 0 |
.connector = connector, |
.edid = edid, |
}; |
|
for (i = 0; i < EDID_STD_TIMINGS; i++) { |
2313,7 → 2354,8 |
add_cvt_modes(struct drm_connector *connector, struct edid *edid) |
{ |
struct detailed_mode_closure closure = { |
connector, edid, 0, 0, 0 |
.connector = connector, |
.edid = edid, |
}; |
|
if (version_greater(edid, 1, 2)) |
2357,11 → 2399,10 |
u32 quirks) |
{ |
struct detailed_mode_closure closure = { |
connector, |
edid, |
1, |
quirks, |
0 |
.connector = connector, |
.edid = edid, |
.preferred = 1, |
.quirks = quirks, |
}; |
|
if (closure.preferred && !version_greater(edid, 1, 3)) |
2386,7 → 2427,7 |
/* |
* Search EDID for CEA extension block. |
*/ |
static u8 *drm_find_cea_extension(struct edid *edid) |
static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) |
{ |
u8 *edid_ext = NULL; |
int i; |
2398,7 → 2439,7 |
/* Find CEA extension */ |
for (i = 0; i < edid->extensions; i++) { |
edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); |
if (edid_ext[0] == CEA_EXT) |
if (edid_ext[0] == ext_id) |
break; |
} |
|
2408,6 → 2449,16 |
return edid_ext; |
} |
|
static u8 *drm_find_cea_extension(struct edid *edid) |
{ |
return drm_find_edid_extension(edid, CEA_EXT); |
} |
|
static u8 *drm_find_displayid_extension(struct edid *edid) |
{ |
return drm_find_edid_extension(edid, DISPLAYID_EXT); |
} |
|
/* |
* Calculate the alternate clock for the CEA mode |
* (60Hz vs. 59.94Hz etc.) |
3125,9 → 3176,12 |
} |
} |
eld[5] |= sad_count << 4; |
eld[2] = (20 + mnl + sad_count * 3 + 3) / 4; |
|
DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count); |
eld[DRM_ELD_BASELINE_ELD_LEN] = |
DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); |
|
DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", |
drm_eld_size(eld), sad_count); |
} |
EXPORT_SYMBOL(drm_edid_to_eld); |
|
3433,10 → 3487,10 |
/** |
* drm_assign_hdmi_deep_color_info - detect whether monitor supports |
* hdmi deep color modes and update drm_display_info if so. |
* |
* @edid: monitor EDID information |
* @info: Updated with maximum supported deep color bpc and color format |
* if deep color supported. |
* @connector: DRM connector, used only for debug output |
* |
* Parse the CEA extension according to CEA-861-B. |
* Return true if HDMI deep color supported, false if not or unknown. |
3865,3 → 3919,123 |
return 0; |
} |
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); |
|
static int drm_parse_display_id(struct drm_connector *connector, |
u8 *displayid, int length, |
bool is_edid_extension) |
{ |
/* if this is an EDID extension the first byte will be 0x70 */ |
int idx = 0; |
struct displayid_hdr *base; |
struct displayid_block *block; |
u8 csum = 0; |
int i; |
|
if (is_edid_extension) |
idx = 1; |
|
base = (struct displayid_hdr *)&displayid[idx]; |
|
DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", |
base->rev, base->bytes, base->prod_id, base->ext_count); |
|
if (base->bytes + 5 > length - idx) |
return -EINVAL; |
|
for (i = idx; i <= base->bytes + 5; i++) { |
csum += displayid[i]; |
} |
if (csum) { |
DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum); |
return -EINVAL; |
} |
|
block = (struct displayid_block *)&displayid[idx + 4]; |
DRM_DEBUG_KMS("block id %d, rev %d, len %d\n", |
block->tag, block->rev, block->num_bytes); |
|
switch (block->tag) { |
case DATA_BLOCK_TILED_DISPLAY: { |
struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; |
|
u16 w, h; |
u8 tile_v_loc, tile_h_loc; |
u8 num_v_tile, num_h_tile; |
struct drm_tile_group *tg; |
|
w = tile->tile_size[0] | tile->tile_size[1] << 8; |
h = tile->tile_size[2] | tile->tile_size[3] << 8; |
|
num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); |
num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); |
tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); |
tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); |
|
connector->has_tile = true; |
if (tile->tile_cap & 0x80) |
connector->tile_is_single_monitor = true; |
|
connector->num_h_tile = num_h_tile + 1; |
connector->num_v_tile = num_v_tile + 1; |
connector->tile_h_loc = tile_h_loc; |
connector->tile_v_loc = tile_v_loc; |
connector->tile_h_size = w + 1; |
connector->tile_v_size = h + 1; |
|
DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); |
DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); |
DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", |
num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); |
DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); |
|
tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); |
if (!tg) { |
tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); |
} |
if (!tg) |
return -ENOMEM; |
|
if (connector->tile_group != tg) { |
/* if we haven't got a pointer, |
take the reference, drop ref to old tile group */ |
if (connector->tile_group) { |
drm_mode_put_tile_group(connector->dev, connector->tile_group); |
} |
connector->tile_group = tg; |
} else |
/* if same tile group, then release the ref we just took. */ |
drm_mode_put_tile_group(connector->dev, tg); |
} |
break; |
default: |
printk("unknown displayid tag %d\n", block->tag); |
break; |
} |
return 0; |
} |
|
static void drm_get_displayid(struct drm_connector *connector, |
struct edid *edid) |
{ |
void *displayid = NULL; |
int ret; |
connector->has_tile = false; |
displayid = drm_find_displayid_extension(edid); |
if (!displayid) { |
/* drop reference to any tile group we had */ |
goto out_drop_ref; |
} |
|
ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true); |
if (ret < 0) |
goto out_drop_ref; |
if (!connector->has_tile) |
goto out_drop_ref; |
return; |
out_drop_ref: |
if (connector->tile_group) { |
drm_mode_put_tile_group(connector->dev, connector->tile_group); |
connector->tile_group = NULL; |
} |
return; |
} |