diff options
author | Dave Airlie <airlied@redhat.com> | 2022-04-12 17:39:45 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2022-04-12 17:44:27 +1000 |
commit | b85ffe47c4ec172214a38b7e7087c60582c488f0 (patch) | |
tree | 128168c955a62a870f7da7cfda94e9299a076486 /drivers/gpu/drm/drm_edid.c | |
parent | ce522ba9ef7e2d9fb22a39eb3371c0c64e2a433e (diff) | |
parent | c8d4c18bfbc4ab467188dbe45cc8155759f49d9e (diff) | |
download | linux-b85ffe47c4ec172214a38b7e7087c60582c488f0.tar.bz2 |
Merge tag 'drm-misc-next-2022-04-07' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.19:
UAPI Changes:
Cross-subsystem Changes:
Core Changes:
- atomic: Add atomic_print_state to private objects
- edid: Constify the EDID parsing API, rework of the API
- dma-buf: Add dma_resv_replace_fences, dma_resv_get_singleton, make
dma_resv_excl_fence private
- format: Support monochrome formats
- fbdev: fixes for cfb_imageblit and sys_imageblit, pagelist
corruption fix
- selftests: several small fixes
- ttm: Rework bulk move handling
Driver Changes:
- Switch all relevant drivers to drm_mode_copy or drm_mode_duplicate
- bridge: conversions to devm_drm_of_get_bridge and panel_bridge,
autosuspend for analogix_dp, audio support for it66121, DSI to DPI
support for tc358767, PLL fixes and I2C support for icn6211
- bridge_connector: Enable HPD if supported
- etnaviv: fencing improvements
- gma500: GEM and GTT improvements, connector handling fixes
- komeda: switch to plane reset helper
- mediatek: MIPI DSI improvements
- omapdrm: GEM improvements
- panel: DT bindings fixes for st7735r, few fixes for ssd130x, new
panels: ltk035c5444t, B133UAN01, NV3052C
- qxl: Allow to run on arm64
- sysfb: Kconfig rework, support for VESA graphic mode selection
- vc4: Add a tracepoint for CL submissions, HDMI YUV output,
HDMI and clock improvements
- virtio: Remove restriction of non-zero blob_flags,
- vmwgfx: support for CursorMob and CursorBypass 4, various
improvements and small fixes
[airlied: fixup conflict with newvision panel callbacks]
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20220407085940.pnflvjojs4qw4b77@houat
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 620 |
1 files changed, 352 insertions, 268 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index cc7bd58369df..04e818ecd662 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -97,7 +97,7 @@ static int oui(u8 first, u8 second, u8 third) struct detailed_mode_closure { struct drm_connector *connector; - struct edid *edid; + const struct edid *edid; bool preferred; u32 quirks; int modes; @@ -1572,6 +1572,11 @@ static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +static void edid_header_fix(void *edid) +{ + memcpy(edid, edid_header, sizeof(edid_header)); +} + /** * drm_edid_header_is_valid - sanity check the header of the base EDID block * @raw_edid: pointer to raw base EDID block @@ -1580,13 +1585,15 @@ static const u8 edid_header[] = { * * Return: 8 if the header is perfect, down to 0 if it's totally wrong. */ -int drm_edid_header_is_valid(const u8 *raw_edid) +int drm_edid_header_is_valid(const void *_edid) { + const struct edid *edid = _edid; int i, score = 0; - for (i = 0; i < sizeof(edid_header); i++) - if (raw_edid[i] == edid_header[i]) + for (i = 0; i < sizeof(edid_header); i++) { + if (edid->header[i] == edid_header[i]) score++; + } return score; } @@ -1597,33 +1604,37 @@ module_param_named(edid_fixup, edid_fixup, int, 0400); MODULE_PARM_DESC(edid_fixup, "Minimum number of valid EDID header bytes (0-8, default 6)"); -static int drm_edid_block_checksum(const u8 *raw_edid) +static int edid_block_compute_checksum(const void *_block) { + const u8 *block = _block; int i; u8 csum = 0, crc = 0; for (i = 0; i < EDID_LENGTH - 1; i++) - csum += raw_edid[i]; + csum += block[i]; crc = 0x100 - csum; return crc; } -static bool drm_edid_block_checksum_diff(const u8 *raw_edid, u8 real_checksum) +static int edid_block_get_checksum(const void *_block) { - if (raw_edid[EDID_LENGTH - 1] != real_checksum) - return true; - else - return false; + const struct edid *block = _block; + + return block->checksum; } -static bool drm_edid_is_zero(const u8 *in_edid, int length) +static int edid_block_tag(const void *_block) { - if (memchr_inv(in_edid, 0, length)) - return false; + const u8 *block = _block; - return true; + return block[0]; +} + +static bool edid_is_zero(const void *edid, int length) +{ + return !memchr_inv(edid, 0, length); } /** @@ -1657,10 +1668,62 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2) } EXPORT_SYMBOL(drm_edid_are_equal); +enum edid_block_status { + EDID_BLOCK_OK = 0, + EDID_BLOCK_NULL, + EDID_BLOCK_HEADER_CORRUPT, + EDID_BLOCK_HEADER_REPAIR, + EDID_BLOCK_HEADER_FIXED, + EDID_BLOCK_CHECKSUM, + EDID_BLOCK_VERSION, +}; + +static enum edid_block_status edid_block_check(const void *_block, + bool is_base_block) +{ + const struct edid *block = _block; + + if (!block) + return EDID_BLOCK_NULL; + + if (is_base_block) { + int score = drm_edid_header_is_valid(block); + + if (score < clamp(edid_fixup, 0, 8)) + return EDID_BLOCK_HEADER_CORRUPT; + + if (score < 8) + return EDID_BLOCK_HEADER_REPAIR; + } + + if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) + return EDID_BLOCK_CHECKSUM; + + if (is_base_block) { + if (block->version != 1) + return EDID_BLOCK_VERSION; + } + + return EDID_BLOCK_OK; +} + +static bool edid_block_status_valid(enum edid_block_status status, int tag) +{ + return status == EDID_BLOCK_OK || + status == EDID_BLOCK_HEADER_FIXED || + (status == EDID_BLOCK_CHECKSUM && tag == CEA_EXT); +} + +static bool edid_block_valid(const void *block, bool base) +{ + return edid_block_status_valid(edid_block_check(block, base), + edid_block_tag(block)); +} + /** * drm_edid_block_valid - Sanity check the EDID block (base or extension) * @raw_edid: pointer to raw EDID block - * @block: type of block to validate (0 for base, extension otherwise) + * @block_num: type of block to validate (0 for base, extension otherwise) * @print_bad_edid: if true, dump bad EDID blocks to the console * @edid_corrupt: if true, the header or checksum is invalid * @@ -1669,88 +1732,70 @@ EXPORT_SYMBOL(drm_edid_are_equal); * * Return: True if the block is valid, false otherwise. */ -bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, +bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid, bool *edid_corrupt) { - u8 csum; - struct edid *edid = (struct edid *)raw_edid; + struct edid *block = (struct edid *)_block; + enum edid_block_status status; + bool is_base_block = block_num == 0; + bool valid; - if (WARN_ON(!raw_edid)) + if (WARN_ON(!block)) return false; - if (edid_fixup > 8 || edid_fixup < 0) - edid_fixup = 6; + status = edid_block_check(block, is_base_block); + if (status == EDID_BLOCK_HEADER_REPAIR) { + DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); + edid_header_fix(block); - if (block == 0) { - int score = drm_edid_header_is_valid(raw_edid); - - if (score == 8) { - if (edid_corrupt) - *edid_corrupt = false; - } else if (score >= edid_fixup) { - /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 - * The corrupt flag needs to be set here otherwise, the - * fix-up code here will correct the problem, the - * checksum is correct and the test fails - */ - if (edid_corrupt) - *edid_corrupt = true; - DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); - memcpy(raw_edid, edid_header, sizeof(edid_header)); - } else { - if (edid_corrupt) - *edid_corrupt = true; - goto bad; - } + /* Retry with fixed header, update status if that worked. */ + status = edid_block_check(block, is_base_block); + if (status == EDID_BLOCK_OK) + status = EDID_BLOCK_HEADER_FIXED; } - csum = drm_edid_block_checksum(raw_edid); - if (drm_edid_block_checksum_diff(raw_edid, csum)) { - if (edid_corrupt) + if (edid_corrupt) { + /* + * Unknown major version isn't corrupt but we can't use it. Only + * the base block can reset edid_corrupt to false. + */ + if (is_base_block && + (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION)) + *edid_corrupt = false; + else if (status != EDID_BLOCK_OK) *edid_corrupt = true; - - /* allow CEA to slide through, switches mangle this */ - if (raw_edid[0] == CEA_EXT) { - DRM_DEBUG("EDID checksum is invalid, remainder is %d\n", csum); - DRM_DEBUG("Assuming a KVM switch modified the CEA block but left the original checksum\n"); - } else { - if (print_bad_edid) - DRM_NOTE("EDID checksum is invalid, remainder is %d\n", csum); - - goto bad; - } } - /* per-block-type checks */ - switch (raw_edid[0]) { - case 0: /* base */ - if (edid->version != 1) { - DRM_NOTE("EDID has major version %d, instead of 1\n", edid->version); - goto bad; + /* Determine whether we can use this block with this status. */ + valid = edid_block_status_valid(status, edid_block_tag(block)); + + /* Some fairly random status printouts. */ + if (status == EDID_BLOCK_CHECKSUM) { + if (valid) { + DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n", + edid_block_compute_checksum(block)); + DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n"); + } else if (print_bad_edid) { + DRM_NOTE("EDID block checksum is invalid, remainder is %d\n", + edid_block_compute_checksum(block)); } - - if (edid->revision > 4) - DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); - break; - - default: - break; + } else if (status == EDID_BLOCK_VERSION) { + DRM_NOTE("EDID has major version %d, instead of 1\n", + block->version); } - return true; - -bad: - if (print_bad_edid) { - if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) { + if (!valid && print_bad_edid) { + if (edid_is_zero(block, EDID_LENGTH)) { pr_notice("EDID block is all zeroes\n"); } else { pr_notice("Raw EDID:\n"); print_hex_dump(KERN_NOTICE, " \t", DUMP_PREFIX_NONE, 16, 1, - raw_edid, EDID_LENGTH, false); + block, EDID_LENGTH, false); } } - return false; + + return valid; } EXPORT_SYMBOL(drm_edid_block_valid); @@ -1778,6 +1823,34 @@ bool drm_edid_is_valid(struct edid *edid) } EXPORT_SYMBOL(drm_edid_is_valid); +static struct edid *edid_filter_invalid_blocks(const struct edid *edid, + int invalid_blocks) +{ + struct edid *new, *dest_block; + int valid_extensions = edid->extensions - invalid_blocks; + int i; + + new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL); + if (!new) + goto out; + + dest_block = new; + for (i = 0; i <= edid->extensions; i++) { + const void *block = edid + i; + + if (edid_block_valid(block, i == 0)) + memcpy(dest_block++, block, EDID_LENGTH); + } + + new->extensions = valid_extensions; + new->checksum = edid_block_compute_checksum(new); + +out: + kfree(edid); + + return new; +} + #define DDC_SEGMENT_ADDR 0x30 /** * drm_do_probe_ddc_edid() - get EDID information via I2C @@ -1859,7 +1932,7 @@ static void connector_bad_edid(struct drm_connector *connector, /* Calculate real checksum for the last edid extension block data */ if (last_block < num_blocks) connector->real_edid_checksum = - drm_edid_block_checksum(edid + last_block * EDID_LENGTH); + edid_block_compute_checksum(edid + last_block * EDID_LENGTH); if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS)) return; @@ -1869,7 +1942,7 @@ static void connector_bad_edid(struct drm_connector *connector, u8 *block = edid + i * EDID_LENGTH; char prefix[20]; - if (drm_edid_is_zero(block, EDID_LENGTH)) + if (edid_is_zero(block, EDID_LENGTH)) sprintf(prefix, "\t[%02x] ZERO ", i); else if (!drm_edid_block_valid(block, i, false, NULL)) sprintf(prefix, "\t[%02x] BAD ", i); @@ -1934,25 +2007,25 @@ static struct edid *drm_do_get_edid_base_block(struct drm_connector *connector, int *null_edid_counter = connector ? &connector->null_edid_counter : NULL; bool *edid_corrupt = connector ? &connector->edid_corrupt : NULL; void *edid; - int i; + int try; edid = kmalloc(EDID_LENGTH, GFP_KERNEL); if (edid == NULL) return NULL; /* base block fetch */ - for (i = 0; i < 4; i++) { + for (try = 0; try < 4; try++) { if (get_edid_block(data, edid, 0, EDID_LENGTH)) goto out; if (drm_edid_block_valid(edid, 0, false, edid_corrupt)) break; - if (i == 0 && drm_edid_is_zero(edid, EDID_LENGTH)) { + if (try == 0 && edid_is_zero(edid, EDID_LENGTH)) { if (null_edid_counter) (*null_edid_counter)++; goto carp; } } - if (i == 4) + if (try == 4) goto carp; return edid; @@ -1990,71 +2063,47 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, size_t len), void *data) { - int i, j = 0, valid_extensions = 0; - u8 *edid, *new; - struct edid *override; + int j, invalid_blocks = 0; + struct edid *edid, *new, *override; override = drm_get_override_edid(connector); if (override) return override; - edid = (u8 *)drm_do_get_edid_base_block(connector, get_edid_block, data); + edid = drm_do_get_edid_base_block(connector, get_edid_block, data); if (!edid) return NULL; - /* if there's no extensions or no connector, we're done */ - valid_extensions = edid[0x7e]; - if (valid_extensions == 0) - return (struct edid *)edid; + if (edid->extensions == 0) + return edid; - new = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); + new = krealloc(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); if (!new) goto out; edid = new; - for (j = 1; j <= edid[0x7e]; j++) { - u8 *block = edid + j * EDID_LENGTH; + for (j = 1; j <= edid->extensions; j++) { + void *block = edid + j; + int try; - for (i = 0; i < 4; i++) { + for (try = 0; try < 4; try++) { if (get_edid_block(data, block, j, EDID_LENGTH)) goto out; if (drm_edid_block_valid(block, j, false, NULL)) break; } - if (i == 4) - valid_extensions--; + if (try == 4) + invalid_blocks++; } - if (valid_extensions != edid[0x7e]) { - u8 *base; + if (invalid_blocks) { + connector_bad_edid(connector, (u8 *)edid, edid->extensions + 1); - connector_bad_edid(connector, edid, edid[0x7e] + 1); - - edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; - edid[0x7e] = valid_extensions; - - new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, - GFP_KERNEL); - if (!new) - goto out; - - base = new; - for (i = 0; i <= edid[0x7e]; i++) { - u8 *block = edid + i * EDID_LENGTH; - - if (!drm_edid_block_valid(block, i, false, NULL)) - continue; - - memcpy(base, block, EDID_LENGTH); - base += EDID_LENGTH; - } - - kfree(edid); - edid = new; + edid = edid_filter_invalid_blocks(edid, invalid_blocks); } - return (struct edid *)edid; + return edid; out: kfree(edid); @@ -2150,7 +2199,7 @@ static u32 edid_extract_panel_id(const struct edid *edid) u32 drm_edid_get_panel_id(struct i2c_adapter *adapter) { - struct edid *edid; + const struct edid *edid; u32 panel_id; edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter); @@ -2331,52 +2380,58 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_find_dmt); -static bool is_display_descriptor(const u8 d[18], u8 tag) +static bool is_display_descriptor(const struct detailed_timing *descriptor, u8 type) { - return d[0] == 0x00 && d[1] == 0x00 && - d[2] == 0x00 && d[3] == tag; + BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.pad1) != 2); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.type) != 3); + + return descriptor->pixel_clock == 0 && + descriptor->data.other_data.pad1 == 0 && + descriptor->data.other_data.type == type; } -static bool is_detailed_timing_descriptor(const u8 d[18]) +static bool is_detailed_timing_descriptor(const struct detailed_timing *descriptor) { - return d[0] != 0x00 || d[1] != 0x00; + BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0); + + return descriptor->pixel_clock != 0; } -typedef void detailed_cb(struct detailed_timing *timing, void *closure); +typedef void detailed_cb(const struct detailed_timing *timing, void *closure); static void -cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +cea_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure) { int i, n; u8 d = ext[0x02]; - u8 *det_base = ext + d; + const u8 *det_base = ext + d; if (d < 4 || d > 127) return; n = (127 - d) / 18; for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); + cb((const struct detailed_timing *)(det_base + 18 * i), closure); } static void -vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) +vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure) { unsigned int i, n = min((int)ext[0x02], 6); - u8 *det_base = ext + 5; + const u8 *det_base = ext + 5; if (ext[0x01] != 1) return; /* unknown version */ for (i = 0; i < n; i++) - cb((struct detailed_timing *)(det_base + 18 * i), closure); + cb((const struct detailed_timing *)(det_base + 18 * i), closure); } static void -drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) +drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure) { int i; - struct edid *edid = (struct edid *)raw_edid; if (edid == NULL) return; @@ -2384,8 +2439,8 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) for (i = 0; i < EDID_DETAILED_TIMINGS; i++) cb(&(edid->detailed_timings[i]), closure); - for (i = 1; i <= raw_edid[0x7e]; i++) { - u8 *ext = raw_edid + (i * EDID_LENGTH); + for (i = 1; i <= edid->extensions; i++) { + const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH); switch (*ext) { case CEA_EXT: @@ -2401,25 +2456,29 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) } static void -is_rb(struct detailed_timing *t, void *data) +is_rb(const struct detailed_timing *descriptor, void *data) { - u8 *r = (u8 *)t; + bool *res = data; - if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) return; - if (r[15] & 0x10) - *(bool *)data = true; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.cvt.flags) != 15); + + if (descriptor->data.other_data.data.range.flags == DRM_EDID_CVT_SUPPORT_FLAG && + descriptor->data.other_data.data.range.formula.cvt.flags & 0x10) + *res = true; } /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ static bool -drm_monitor_supports_rb(struct edid *edid) +drm_monitor_supports_rb(const struct edid *edid) { if (edid->revision >= 4) { bool ret = false; - drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); + drm_for_each_detailed_block(edid, is_rb, &ret); return ret; } @@ -2427,68 +2486,85 @@ drm_monitor_supports_rb(struct edid *edid) } static void -find_gtf2(struct detailed_timing *t, void *data) +find_gtf2(const struct detailed_timing *descriptor, void *data) { - u8 *r = (u8 *)t; + const struct detailed_timing **res = data; - if (!is_display_descriptor(r, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) return; - if (r[10] == 0x02) - *(u8 **)data = r; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); + + if (descriptor->data.other_data.data.range.flags == 0x02) + *res = descriptor; } /* Secondary GTF curve kicks in above some break frequency */ static int -drm_gtf2_hbreak(struct edid *edid) +drm_gtf2_hbreak(const struct edid *edid) { - u8 *r = NULL; + const struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[12] * 2) : 0; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12); + + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.hfreq_start_khz * 2 : 0; } static int -drm_gtf2_2c(struct edid *edid) +drm_gtf2_2c(const struct edid *edid) { - u8 *r = NULL; + const struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[13] : 0; + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13); + + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.c : 0; } static int -drm_gtf2_m(struct edid *edid) +drm_gtf2_m(const struct edid *edid) { - u8 *r = NULL; + const struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? (r[15] << 8) + r[14] : 0; + return descriptor ? le16_to_cpu(descriptor->data.other_data.data.range.formula.gtf2.m) : 0; } static int -drm_gtf2_k(struct edid *edid) +drm_gtf2_k(const struct edid *edid) { - u8 *r = NULL; + const struct detailed_timing *descriptor = NULL; + + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16); - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[16] : 0; + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.k : 0; } static int -drm_gtf2_2j(struct edid *edid) +drm_gtf2_2j(const struct edid *edid) { - u8 *r = NULL; + const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); - return r ? r[17] : 0; + drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17); + + return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0; } /** * standard_timing_level - get std. timing level(CVT/GTF/DMT) * @edid: EDID block to scan */ -static int standard_timing_level(struct edid *edid) +static int standard_timing_level(const struct edid *edid) { if (edid->revision >= 2) { if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) @@ -2531,8 +2607,8 @@ static int drm_mode_hsync(const struct drm_display_mode *mode) * and convert them into a real mode using CVT/GTF/DMT. */ static struct drm_display_mode * -drm_mode_std(struct drm_connector *connector, struct edid *edid, - struct std_timing *t) +drm_mode_std(struct drm_connector *connector, const struct edid *edid, + const struct std_timing *t) { struct drm_device *dev = connector->dev; struct drm_display_mode *m, *mode = NULL; @@ -2650,7 +2726,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid, */ static void drm_mode_do_interlace_quirk(struct drm_display_mode *mode, - struct detailed_pixel_timing *pt) + const struct detailed_pixel_timing *pt) { int i; static const struct { @@ -2693,12 +2769,12 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, * return a new struct drm_display_mode. */ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, - struct edid *edid, - struct detailed_timing *timing, + const struct edid *edid, + const struct detailed_timing *timing, u32 quirks) { struct drm_display_mode *mode; - struct detailed_pixel_timing *pt = &timing->data.pixel_data; + const struct detailed_pixel_timing *pt = &timing->data.pixel_data; unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; @@ -2740,9 +2816,9 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return NULL; if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) - timing->pixel_clock = cpu_to_le16(1088); - - mode->clock = le16_to_cpu(timing->pixel_clock) * 10; + mode->clock = 1088 * 10; + else + mode->clock = le16_to_cpu(timing->pixel_clock) * 10; mode->hdisplay = hactive; mode->hsync_start = mode->hdisplay + hsync_offset; @@ -2763,14 +2839,14 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, drm_mode_do_interlace_quirk(mode, pt); if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { - pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; + mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; + } else { + mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? + DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? + DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; } - mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? - DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; - mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? - DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; - set_size: mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; @@ -2793,7 +2869,7 @@ set_size: static bool mode_in_hsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) + const struct edid *edid, const u8 *t) { int hsync, hmin, hmax; @@ -2810,7 +2886,7 @@ mode_in_hsync_range(const struct drm_display_mode *mode, static bool mode_in_vsync_range(const struct drm_display_mode *mode, - struct edid *edid, u8 *t) + const struct edid *edid, const u8 *t) { int vsync, vmin, vmax; @@ -2826,7 +2902,7 @@ mode_in_vsync_range(const struct drm_display_mode *mode, } static u32 -range_pixel_clock(struct edid *edid, u8 *t) +range_pixel_clock(const struct edid *edid, const u8 *t) { /* unspecified */ if (t[9] == 0 || t[9] == 255) @@ -2841,11 +2917,11 @@ range_pixel_clock(struct edid *edid, u8 *t) } static bool -mode_in_range(const struct drm_display_mode *mode, struct edid *edid, - struct detailed_timing *timing) +mode_in_range(const struct drm_display_mode *mode, const struct edid *edid, + const struct detailed_timing *timing) { u32 max_clock; - u8 *t = (u8 *)timing; + const u8 *t = (const u8 *)timing; if (!mode_in_hsync_range(mode, edid, t)) return false; @@ -2887,8 +2963,8 @@ static bool valid_inferred_mode(const struct drm_connector *connector, } static int -drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) +drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -2922,8 +2998,8 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode) } static int -drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) +drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -2951,8 +3027,8 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, } static int -drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, - struct detailed_timing *timing) +drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -2981,13 +3057,13 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, } static void -do_inferred_modes(struct detailed_timing *timing, void *c) +do_inferred_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; - struct detailed_data_monitor_range *range = &data->data.range; + const struct detailed_non_pixel *data = &timing->data.other_data; + const struct detailed_data_monitor_range *range = &data->data.range; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE)) return; closure->modes += drm_dmt_modes_for_range(closure->connector, @@ -3019,7 +3095,7 @@ do_inferred_modes(struct detailed_timing *timing, void *c) } static int -add_inferred_modes(struct drm_connector *connector, struct edid *edid) +add_inferred_modes(struct drm_connector *connector, const struct edid *edid) { struct detailed_mode_closure closure = { .connector = connector, @@ -3027,18 +3103,17 @@ add_inferred_modes(struct drm_connector *connector, struct edid *edid) }; if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, - &closure); + drm_for_each_detailed_block(edid, do_inferred_modes, &closure); return closure.modes; } static int -drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) +drm_est3_modes(struct drm_connector *connector, const struct detailed_timing *timing) { int i, j, m, modes = 0; struct drm_display_mode *mode; - u8 *est = ((u8 *)timing) + 6; + const u8 *est = ((const u8 *)timing) + 6; for (i = 0; i < 6; i++) { for (j = 7; j >= 0; j--) { @@ -3063,11 +3138,11 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) } static void -do_established_modes(struct detailed_timing *timing, void *c) +do_established_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_EST_TIMINGS)) + if (!is_display_descriptor(timing, EDID_DETAIL_EST_TIMINGS)) return; closure->modes += drm_est3_modes(closure->connector, timing); @@ -3082,7 +3157,7 @@ do_established_modes(struct detailed_timing *timing, void *c) * (defined above). Tease them out and add them to the global modes list. */ static int -add_established_modes(struct drm_connector *connector, struct edid *edid) +add_established_modes(struct drm_connector *connector, const struct edid *edid) { struct drm_device *dev = connector->dev; unsigned long est_bits = edid->established_timings.t1 | @@ -3107,26 +3182,26 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) } if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, - do_established_modes, &closure); + drm_for_each_detailed_block(edid, do_established_modes, + &closure); return modes + closure.modes; } static void -do_standard_modes(struct detailed_timing *timing, void *c) +do_standard_modes(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - struct detailed_non_pixel *data = &timing->data.other_data; + const struct detailed_non_pixel *data = &timing->data.other_data; struct drm_connector *connector = closure->connector; - struct edid *edid = closure->edid; + const struct edid *edid = closure->edid; int i; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_STD_MODES)) + if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES)) return; for (i = 0; i < 6; i++) { - struct std_timing *std = &data->data.timings[i]; + const struct std_timing *std = &data->data.timings[i]; struct drm_display_mode *newmode; newmode = drm_mode_std(connector, edid, std); @@ -3146,7 +3221,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) * GTF or CVT. Grab them from @edid and add them to the list. */ static int -add_standard_modes(struct drm_connector *connector, struct edid *edid) +add_standard_modes(struct drm_connector *connector, const struct edid *edid) { int i, modes = 0; struct detailed_mode_closure closure = { @@ -3166,7 +3241,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) } if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_standard_modes, + drm_for_each_detailed_block(edid, do_standard_modes, &closure); /* XXX should also look for standard codes in VTB blocks */ @@ -3175,12 +3250,12 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) } static int drm_cvt_modes(struct drm_connector *connector, - struct detailed_timing *timing) + const struct detailed_timing *timing) { int i, j, modes = 0; struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; - struct cvt_timing *cvt; + const struct cvt_timing *cvt; const int rates[] = { 60, 85, 75, 60, 50 }; const u8 empty[3] = { 0, 0, 0 }; @@ -3227,18 +3302,18 @@ static int drm_cvt_modes(struct drm_connector *connector, } static void -do_cvt_mode(struct detailed_timing *timing, void *c) +do_cvt_mode(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_CVT_3BYTE)) + if (!is_display_descriptor(timing, EDID_DETAIL_CVT_3BYTE)) return; closure->modes += drm_cvt_modes(closure->connector, timing); } static int -add_cvt_modes(struct drm_connector *connector, struct edid *edid) +add_cvt_modes(struct drm_connector *connector, const struct edid *edid) { struct detailed_mode_closure closure = { .connector = connector, @@ -3246,7 +3321,7 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid) }; if (version_greater(edid, 1, 2)) - drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); + drm_for_each_detailed_block(edid, do_cvt_mode, &closure); /* XXX should also look for CVT codes in VTB blocks */ @@ -3256,12 +3331,12 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid) static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); static void -do_detailed_mode(struct detailed_timing *timing, void *c) +do_detailed_mode(const struct detailed_timing *timing, void *c) { struct detailed_mode_closure *closure = c; struct drm_display_mode *newmode; - if (!is_detailed_timing_descriptor((const u8 *)timing)) + if (!is_detailed_timing_descriptor(timing)) return; newmode = drm_mode_detailed(closure->connector->dev, @@ -3292,7 +3367,7 @@ do_detailed_mode(struct detailed_timing *timing, void *c) * @quirks: quirks to apply */ static int -add_detailed_modes(struct drm_connector *connector, struct edid *edid, +add_detailed_modes(struct drm_connector *connector, const struct edid *edid, u32 quirks) { struct detailed_mode_closure closure = { @@ -3306,7 +3381,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, closure.preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); + drm_for_each_detailed_block(edid, do_detailed_mode, &closure); return closure.modes; } @@ -3341,7 +3416,7 @@ const u8 *drm_find_edid_extension(const struct edid *edid, /* Find CEA extension */ for (i = *ext_index; i < edid->extensions; i++) { edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == ext_id) + if (edid_block_tag(edid_ext) == ext_id) break; } @@ -3638,7 +3713,7 @@ static bool drm_valid_hdmi_vic(u8 vic) } static int -add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) +add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *tmp; @@ -4319,7 +4394,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, } static int -add_cea_modes(struct drm_connector *connector, struct edid *edid) +add_cea_modes(struct drm_connector *connector, const struct edid *edid) { const u8 *cea = drm_find_cea_extension(edid); const u8 *db, *hdmi = NULL, *video = NULL; @@ -4489,23 +4564,25 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) } static void -monitor_name(struct detailed_timing *t, void *data) +monitor_name(const struct detailed_timing *timing, void *data) { - if (!is_display_descriptor((const u8 *)t, EDID_DETAIL_MONITOR_NAME)) + const char **res = data; + + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME)) return; - *(u8 **)data = t->data.other_data.data.str.str; + *res = timing->data.other_data.data.str.str; } -static int get_monitor_name(struct edid *edid, char name[13]) +static int get_monitor_name(const struct edid *edid, char name[13]) { - char *edid_name = NULL; + const char *edid_name = NULL; int mnl; if (!edid || !name) return 0; - drm_for_each_detailed_block((u8 *)edid, monitor_name, &edid_name); + drm_for_each_detailed_block(edid, monitor_name, &edid_name); for (mnl = 0; edid_name && mnl < 13; mnl++) { if (edid_name[mnl] == 0x0a) break; @@ -4523,7 +4600,7 @@ static int get_monitor_name(struct edid *edid, char name[13]) * @bufsize: The size of the name buffer (should be at least 14 chars.) * */ -void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize) +void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize) { int name_length; char buf[13]; @@ -4557,7 +4634,8 @@ static void clear_eld(struct drm_connector *connector) * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The * HDCP and Port_ID ELD fields are left for the graphics driver to fill in. */ -static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) +static void drm_edid_to_eld(struct drm_connector *connector, + const struct edid *edid) { uint8_t *eld = connector->eld; const u8 *cea; @@ -4653,7 +4731,7 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) * * Return: The number of found SADs or negative number on error. */ -int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) +int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads) { int count = 0; int i, start, end, dbl; @@ -4715,7 +4793,7 @@ EXPORT_SYMBOL(drm_edid_to_sad); * Return: The number of found Speaker Allocation Blocks or negative number on * error. */ -int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) +int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb) { int count = 0; int i, start, end, dbl; @@ -4810,7 +4888,7 @@ EXPORT_SYMBOL(drm_av_sync_delay); * * Return: True if the monitor is HDMI, false if not or unknown. */ -bool drm_detect_hdmi_monitor(struct edid *edid) +bool drm_detect_hdmi_monitor(const struct edid *edid) { const u8 *edid_ext; int i; @@ -4848,7 +4926,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); * * Return: True if the monitor supports audio, false otherwise. */ -bool drm_detect_monitor_audio(struct edid *edid) +bool drm_detect_monitor_audio(const struct edid *edid) { const u8 *edid_ext; int i, j; @@ -5219,14 +5297,14 @@ static void drm_parse_cea_ext(struct drm_connector *connector, } static -void get_monitor_range(struct detailed_timing *timing, +void get_monitor_range(const struct detailed_timing *timing, void *info_monitor_range) { struct drm_monitor_range_info *monitor_range = info_monitor_range; const struct detailed_non_pixel *data = &timing->data.other_data; const struct detailed_data_monitor_range *range = &data->data.range; - if (!is_display_descriptor((const u8 *)timing, EDID_DETAIL_MONITOR_RANGE)) + if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE)) return; /* @@ -5251,7 +5329,7 @@ void drm_get_monitor_range(struct drm_connector *connector, if (!version_greater(edid, 1, 1)) return; - drm_for_each_detailed_block((u8 *)edid, get_monitor_range, + drm_for_each_detailed_block(edid, get_monitor_range, &info->monitor_range); DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n", @@ -5515,7 +5593,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, } static int add_displayid_detailed_modes(struct drm_connector *connector, - struct edid *edid) + const struct edid *edid) { const struct displayid_block *block; struct displayid_iter iter; @@ -5532,18 +5610,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -/** - * drm_add_edid_modes - add modes from EDID data, if available - * @connector: connector we're probing - * @edid: EDID data - * - * Add the specified modes to the connector's mode list. Also fills out the - * &drm_display_info structure and ELD in @connector with any information which - * can be derived from the edid. - * - * Return: The number of modes added or 0 if we couldn't find any. - */ -int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +static int drm_edid_connector_update(struct drm_connector *connector, + const struct edid *edid) { int num_modes = 0; u32 quirks; @@ -5552,12 +5620,6 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) clear_eld(connector); return 0; } - if (!drm_edid_is_valid(edid)) { - clear_eld(connector); - drm_warn(connector->dev, "%s: EDID invalid.\n", - connector->name); - return 0; - } drm_edid_to_eld(connector, edid); @@ -5609,6 +5671,28 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) return num_modes; } + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @connector: connector we're probing + * @edid: EDID data + * + * Add the specified modes to the connector's mode list. Also fills out the + * &drm_display_info structure and ELD in @connector with any information which + * can be derived from the edid. + * + * Return: The number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +{ + if (edid && !drm_edid_is_valid(edid)) { + drm_warn(connector->dev, "%s: EDID invalid.\n", + connector->name); + edid = NULL; + } + + return drm_edid_connector_update(connector, edid); +} EXPORT_SYMBOL(drm_add_edid_modes); /** |