diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
| -rw-r--r-- | drivers/gpu/drm/drm_edid.c | 2079 |
1 files changed, 1335 insertions, 744 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 4005dab6147d..26bb7710a462 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -29,21 +29,27 @@ */ #include <linux/bitfield.h> +#include <linux/byteorder/generic.h> +#include <linux/cec.h> +#include <linux/export.h> #include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/seq_buf.h> #include <linux/slab.h> #include <linux/vga_switcheroo.h> -#include <drm/drm_displayid.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/drm_encoder.h> #include <drm/drm_print.h> #include "drm_crtc_internal.h" +#include "drm_displayid_internal.h" +#include "drm_internal.h" static int oui(u8 first, u8 second, u8 third) { @@ -61,32 +67,36 @@ static int oui(u8 first, u8 second, u8 third) * on as many displays as possible). */ -/* First detailed mode wrong, use largest 60Hz mode */ -#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) -/* Reported 135MHz pixel clock is too high, needs adjustment */ -#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) -/* Prefer the largest mode at 75 Hz */ -#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) -/* Detail timing is in cm not mm */ -#define EDID_QUIRK_DETAILED_IN_CM (1 << 3) -/* Detailed timing descriptors have bogus size values, so just take the - * maximum size and use that. - */ -#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) -/* use +hsync +vsync for detailed mode */ -#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) -/* Force reduced-blanking timings for detailed modes */ -#define EDID_QUIRK_FORCE_REDUCED_BLANKING (1 << 7) -/* Force 8bpc */ -#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) -/* Force 10bpc */ -#define EDID_QUIRK_FORCE_10BPC (1 << 11) -/* Non desktop display (i.e. HMD) */ -#define EDID_QUIRK_NON_DESKTOP (1 << 12) +enum drm_edid_internal_quirk { + /* First detailed mode wrong, use largest 60Hz mode */ + EDID_QUIRK_PREFER_LARGE_60 = DRM_EDID_QUIRK_NUM, + /* Reported 135MHz pixel clock is too high, needs adjustment */ + EDID_QUIRK_135_CLOCK_TOO_HIGH, + /* Prefer the largest mode at 75 Hz */ + EDID_QUIRK_PREFER_LARGE_75, + /* Detail timing is in cm not mm */ + EDID_QUIRK_DETAILED_IN_CM, + /* Detailed timing descriptors have bogus size values, so just take the + * maximum size and use that. + */ + EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE, + /* use +hsync +vsync for detailed mode */ + EDID_QUIRK_DETAILED_SYNC_PP, + /* Force reduced-blanking timings for detailed modes */ + EDID_QUIRK_FORCE_REDUCED_BLANKING, + /* Force 8bpc */ + EDID_QUIRK_FORCE_8BPC, + /* Force 12bpc */ + EDID_QUIRK_FORCE_12BPC, + /* Force 6bpc */ + EDID_QUIRK_FORCE_6BPC, + /* Force 10bpc */ + EDID_QUIRK_FORCE_10BPC, + /* Non desktop display (i.e. HMD) */ + EDID_QUIRK_NON_DESKTOP, + /* Cap the DSC target bitrate to 15bpp */ + EDID_QUIRK_CAP_DSC_15BPP, +}; #define MICROSOFT_IEEE_OUI 0xca125c @@ -94,10 +104,14 @@ struct detailed_mode_closure { struct drm_connector *connector; const struct drm_edid *drm_edid; bool preferred; - u32 quirks; int modes; }; +struct drm_edid_match_closure { + const struct drm_edid_ident *ident; + bool matched; +}; + #define LEVEL_DMT 0 #define LEVEL_GTF 1 #define LEVEL_GTF2 2 @@ -105,124 +119,147 @@ struct detailed_mode_closure { #define EDID_QUIRK(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _quirks) \ { \ - .panel_id = drm_edid_encode_panel_id(vend_chr_0, vend_chr_1, vend_chr_2, \ - product_id), \ + .ident = { \ + .panel_id = drm_edid_encode_panel_id(vend_chr_0, vend_chr_1, \ + vend_chr_2, product_id), \ + }, \ .quirks = _quirks \ } static const struct edid_quirk { - u32 panel_id; + const struct drm_edid_ident ident; u32 quirks; } edid_quirk_list[] = { /* Acer AL1706 */ - EDID_QUIRK('A', 'C', 'R', 44358, EDID_QUIRK_PREFER_LARGE_60), + EDID_QUIRK('A', 'C', 'R', 44358, BIT(EDID_QUIRK_PREFER_LARGE_60)), /* Acer F51 */ - EDID_QUIRK('A', 'P', 'I', 0x7602, EDID_QUIRK_PREFER_LARGE_60), + EDID_QUIRK('A', 'P', 'I', 0x7602, BIT(EDID_QUIRK_PREFER_LARGE_60)), /* AEO model 0 reports 8 bpc, but is a 6 bpc panel */ - EDID_QUIRK('A', 'E', 'O', 0, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('A', 'E', 'O', 0, BIT(EDID_QUIRK_FORCE_6BPC)), + + /* BenQ GW2765 */ + EDID_QUIRK('B', 'N', 'Q', 0x78d6, BIT(EDID_QUIRK_FORCE_8BPC)), /* BOE model on HP Pavilion 15-n233sl reports 8 bpc, but is a 6 bpc panel */ - EDID_QUIRK('B', 'O', 'E', 0x78b, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('B', 'O', 'E', 0x78b, BIT(EDID_QUIRK_FORCE_6BPC)), /* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */ - EDID_QUIRK('C', 'P', 'T', 0x17df, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('C', 'P', 'T', 0x17df, BIT(EDID_QUIRK_FORCE_6BPC)), /* SDC panel of Lenovo B50-80 reports 8 bpc, but is a 6 bpc panel */ - EDID_QUIRK('S', 'D', 'C', 0x3652, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('S', 'D', 'C', 0x3652, BIT(EDID_QUIRK_FORCE_6BPC)), /* BOE model 0x0771 reports 8 bpc, but is a 6 bpc panel */ - EDID_QUIRK('B', 'O', 'E', 0x0771, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('B', 'O', 'E', 0x0771, BIT(EDID_QUIRK_FORCE_6BPC)), /* Belinea 10 15 55 */ - EDID_QUIRK('M', 'A', 'X', 1516, EDID_QUIRK_PREFER_LARGE_60), - EDID_QUIRK('M', 'A', 'X', 0x77e, EDID_QUIRK_PREFER_LARGE_60), + EDID_QUIRK('M', 'A', 'X', 1516, BIT(EDID_QUIRK_PREFER_LARGE_60)), + EDID_QUIRK('M', 'A', 'X', 0x77e, BIT(EDID_QUIRK_PREFER_LARGE_60)), /* Envision Peripherals, Inc. EN-7100e */ - EDID_QUIRK('E', 'P', 'I', 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH), + EDID_QUIRK('E', 'P', 'I', 59264, BIT(EDID_QUIRK_135_CLOCK_TOO_HIGH)), /* Envision EN2028 */ - EDID_QUIRK('E', 'P', 'I', 8232, EDID_QUIRK_PREFER_LARGE_60), + EDID_QUIRK('E', 'P', 'I', 8232, BIT(EDID_QUIRK_PREFER_LARGE_60)), /* Funai Electronics PM36B */ - EDID_QUIRK('F', 'C', 'M', 13600, EDID_QUIRK_PREFER_LARGE_75 | - EDID_QUIRK_DETAILED_IN_CM), + EDID_QUIRK('F', 'C', 'M', 13600, BIT(EDID_QUIRK_PREFER_LARGE_75) | + BIT(EDID_QUIRK_DETAILED_IN_CM)), + + /* LG 27GP950 */ + EDID_QUIRK('G', 'S', 'M', 0x5bbf, BIT(EDID_QUIRK_CAP_DSC_15BPP)), + + /* LG 27GN950 */ + EDID_QUIRK('G', 'S', 'M', 0x5b9a, BIT(EDID_QUIRK_CAP_DSC_15BPP)), /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */ - EDID_QUIRK('L', 'G', 'D', 764, EDID_QUIRK_FORCE_10BPC), + EDID_QUIRK('L', 'G', 'D', 764, BIT(EDID_QUIRK_FORCE_10BPC)), /* LG Philips LCD LP154W01-A5 */ - EDID_QUIRK('L', 'P', 'L', 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE), - EDID_QUIRK('L', 'P', 'L', 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE), + EDID_QUIRK('L', 'P', 'L', 0, BIT(EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE)), + EDID_QUIRK('L', 'P', 'L', 0x2a00, BIT(EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE)), /* Samsung SyncMaster 205BW. Note: irony */ - EDID_QUIRK('S', 'A', 'M', 541, EDID_QUIRK_DETAILED_SYNC_PP), + EDID_QUIRK('S', 'A', 'M', 541, BIT(EDID_QUIRK_DETAILED_SYNC_PP)), /* Samsung SyncMaster 22[5-6]BW */ - EDID_QUIRK('S', 'A', 'M', 596, EDID_QUIRK_PREFER_LARGE_60), - EDID_QUIRK('S', 'A', 'M', 638, EDID_QUIRK_PREFER_LARGE_60), + EDID_QUIRK('S', 'A', 'M', 596, BIT(EDID_QUIRK_PREFER_LARGE_60)), + EDID_QUIRK('S', 'A', 'M', 638, BIT(EDID_QUIRK_PREFER_LARGE_60)), /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */ - EDID_QUIRK('S', 'N', 'Y', 0x2541, EDID_QUIRK_FORCE_12BPC), + EDID_QUIRK('S', 'N', 'Y', 0x2541, BIT(EDID_QUIRK_FORCE_12BPC)), /* ViewSonic VA2026w */ - EDID_QUIRK('V', 'S', 'C', 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING), + EDID_QUIRK('V', 'S', 'C', 5020, BIT(EDID_QUIRK_FORCE_REDUCED_BLANKING)), /* Medion MD 30217 PG */ - EDID_QUIRK('M', 'E', 'D', 0x7b8, EDID_QUIRK_PREFER_LARGE_75), + EDID_QUIRK('M', 'E', 'D', 0x7b8, BIT(EDID_QUIRK_PREFER_LARGE_75)), /* Lenovo G50 */ - EDID_QUIRK('S', 'D', 'C', 18514, EDID_QUIRK_FORCE_6BPC), + EDID_QUIRK('S', 'D', 'C', 18514, BIT(EDID_QUIRK_FORCE_6BPC)), /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ - EDID_QUIRK('S', 'E', 'C', 0xd033, EDID_QUIRK_FORCE_8BPC), + EDID_QUIRK('S', 'E', 'C', 0xd033, BIT(EDID_QUIRK_FORCE_8BPC)), /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ - EDID_QUIRK('E', 'T', 'R', 13896, EDID_QUIRK_FORCE_8BPC), + EDID_QUIRK('E', 'T', 'R', 13896, BIT(EDID_QUIRK_FORCE_8BPC)), /* Valve Index Headset */ - EDID_QUIRK('V', 'L', 'V', 0x91a8, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b0, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b1, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b2, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b3, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b4, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b5, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b6, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b7, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b8, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91b9, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91ba, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91bb, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91bc, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91bd, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91be, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('V', 'L', 'V', 0x91bf, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('V', 'L', 'V', 0x91a8, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b0, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b1, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b2, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b3, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b4, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b5, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b6, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b7, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b8, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91b9, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91ba, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91bb, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91bc, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91bd, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91be, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('V', 'L', 'V', 0x91bf, BIT(EDID_QUIRK_NON_DESKTOP)), /* HTC Vive and Vive Pro VR Headsets */ - EDID_QUIRK('H', 'V', 'R', 0xaa01, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('H', 'V', 'R', 0xaa02, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('H', 'V', 'R', 0xaa01, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('H', 'V', 'R', 0xaa02, BIT(EDID_QUIRK_NON_DESKTOP)), /* Oculus Rift DK1, DK2, CV1 and Rift S VR Headsets */ - EDID_QUIRK('O', 'V', 'R', 0x0001, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('O', 'V', 'R', 0x0003, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('O', 'V', 'R', 0x0004, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('O', 'V', 'R', 0x0012, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('O', 'V', 'R', 0x0001, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('O', 'V', 'R', 0x0003, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('O', 'V', 'R', 0x0004, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('O', 'V', 'R', 0x0012, BIT(EDID_QUIRK_NON_DESKTOP)), /* Windows Mixed Reality Headsets */ - EDID_QUIRK('A', 'C', 'R', 0x7fce, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('L', 'E', 'N', 0x0408, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('F', 'U', 'J', 0x1970, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('D', 'E', 'L', 0x7fce, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('S', 'E', 'C', 0x144a, EDID_QUIRK_NON_DESKTOP), - EDID_QUIRK('A', 'U', 'S', 0xc102, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('A', 'C', 'R', 0x7fce, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('L', 'E', 'N', 0x0408, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('F', 'U', 'J', 0x1970, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('D', 'E', 'L', 0x7fce, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('S', 'E', 'C', 0x144a, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('A', 'U', 'S', 0xc102, BIT(EDID_QUIRK_NON_DESKTOP)), /* Sony PlayStation VR Headset */ - EDID_QUIRK('S', 'N', 'Y', 0x0704, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('S', 'N', 'Y', 0x0704, BIT(EDID_QUIRK_NON_DESKTOP)), /* Sensics VR Headsets */ - EDID_QUIRK('S', 'E', 'N', 0x1019, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('S', 'E', 'N', 0x1019, BIT(EDID_QUIRK_NON_DESKTOP)), /* OSVR HDK and HDK2 VR Headsets */ - EDID_QUIRK('S', 'V', 'R', 0x1019, EDID_QUIRK_NON_DESKTOP), + EDID_QUIRK('S', 'V', 'R', 0x1019, BIT(EDID_QUIRK_NON_DESKTOP)), + EDID_QUIRK('A', 'U', 'O', 0x1111, BIT(EDID_QUIRK_NON_DESKTOP)), + + /* LQ116M1JW10 displays noise when 8 bpc, but display fine as 6 bpc */ + EDID_QUIRK('S', 'H', 'P', 0x154c, BIT(EDID_QUIRK_FORCE_6BPC)), + + /* + * @drm_edid_internal_quirk entries end here, following with the + * @drm_edid_quirk entries. + */ + + /* HP ZR24w DP AUX DPCD access requires probing to prevent corruption. */ + EDID_QUIRK('H', 'W', 'P', 0x2869, BIT(DRM_EDID_QUIRK_DP_DPCD_PROBE)), }; /* @@ -1572,15 +1609,6 @@ struct drm_edid { const struct edid *edid; }; -static bool version_greater(const struct drm_edid *drm_edid, - u8 version, u8 revision) -{ - const struct edid *edid = drm_edid->edid; - - return edid->version > version || - (edid->version == version && edid->revision > revision); -} - static int edid_hfeeodb_extension_block_count(const struct edid *edid); static int edid_hfeeodb_block_count(const struct edid *edid) @@ -1622,7 +1650,8 @@ static const void *edid_extension_block_data(const struct edid *edid, int index) return edid_block_data(edid, index + 1); } -static int drm_edid_block_count(const struct drm_edid *drm_edid) +/* EDID block count indicated in EDID, may exceed allocated size */ +static int __drm_edid_block_count(const struct drm_edid *drm_edid) { int num_blocks; @@ -1642,12 +1671,18 @@ static int drm_edid_block_count(const struct drm_edid *drm_edid) num_blocks = eeodb; } - /* Limit by allocated size */ - num_blocks = min(num_blocks, (int)drm_edid->size / EDID_LENGTH); - return num_blocks; } +/* EDID block count, limited by allocated size */ +static int drm_edid_block_count(const struct drm_edid *drm_edid) +{ + /* Limit by allocated size */ + return min(__drm_edid_block_count(drm_edid), + (int)drm_edid->size / EDID_LENGTH); +} + +/* EDID extension block count, limited by allocated size */ static int drm_edid_extension_block_count(const struct drm_edid *drm_edid) { return drm_edid_block_count(drm_edid) - 1; @@ -1796,39 +1831,28 @@ static int edid_block_tag(const void *_block) static bool edid_block_is_zero(const void *edid) { - return !memchr_inv(edid, 0, EDID_LENGTH); + return mem_is_zero(edid, EDID_LENGTH); } -/** - * drm_edid_are_equal - compare two edid blobs. - * @edid1: pointer to first blob - * @edid2: pointer to second blob - * This helper can be used during probing to determine if - * edid had changed. - */ -bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2) +static bool drm_edid_eq(const struct drm_edid *drm_edid, + const void *raw_edid, size_t raw_edid_size) { - int edid1_len, edid2_len; - bool edid1_present = edid1 != NULL; - bool edid2_present = edid2 != NULL; + bool edid1_present = drm_edid && drm_edid->edid && drm_edid->size; + bool edid2_present = raw_edid && raw_edid_size; if (edid1_present != edid2_present) return false; - if (edid1) { - edid1_len = edid_size(edid1); - edid2_len = edid_size(edid2); - - if (edid1_len != edid2_len) + if (edid1_present) { + if (drm_edid->size != raw_edid_size) return false; - if (memcmp(edid1, edid2, edid1_len)) + if (memcmp(drm_edid->edid, raw_edid, drm_edid->size)) return false; } return true; } -EXPORT_SYMBOL(drm_edid_are_equal); enum edid_block_status { EDID_BLOCK_OK = 0, @@ -1956,22 +1980,14 @@ static void edid_block_dump(const char *level, const void *block, int block_num) block, EDID_LENGTH, false); } -/** - * drm_edid_block_valid - Sanity check the EDID block (base or extension) - * @_block: pointer to raw EDID block - * @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 - * +/* * Validate a base or extension EDID block and optionally dump bad blocks to * the console. - * - * Return: True if the block is valid, false otherwise. */ -bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid, - bool *edid_corrupt) +static bool drm_edid_block_valid(void *_block, int block_num, bool print_bad_edid, + bool *edid_corrupt) { - struct edid *block = (struct edid *)_block; + struct edid *block = _block; enum edid_block_status status; bool is_base_block = block_num == 0; bool valid; @@ -1981,7 +1997,7 @@ bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid, 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"); + DRM_DEBUG_KMS("Fixing EDID header, your hardware may be failing\n"); edid_header_fix(block); /* Retry with fixed header, update status if that worked. */ @@ -2014,7 +2030,6 @@ bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid, return valid; } -EXPORT_SYMBOL(drm_edid_block_valid); /** * drm_edid_is_valid - sanity check EDID data @@ -2042,6 +2057,36 @@ bool drm_edid_is_valid(struct edid *edid) } EXPORT_SYMBOL(drm_edid_is_valid); +/** + * drm_edid_valid - sanity check EDID data + * @drm_edid: EDID data + * + * Sanity check an EDID. Cross check block count against allocated size and + * checksum the blocks. + * + * Return: True if the EDID data is valid, false otherwise. + */ +bool drm_edid_valid(const struct drm_edid *drm_edid) +{ + int i; + + if (!drm_edid) + return false; + + if (edid_size_by_blocks(__drm_edid_block_count(drm_edid)) != drm_edid->size) + return false; + + for (i = 0; i < drm_edid_block_count(drm_edid); i++) { + const void *block = drm_edid_block_data(drm_edid, i); + + if (!edid_block_valid(block, i == 0)) + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_edid_valid); + static struct edid *edid_filter_invalid_blocks(struct edid *edid, size_t *alloc_size) { @@ -2168,58 +2213,91 @@ static void connector_bad_edid(struct drm_connector *connector, if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS)) return; - drm_dbg_kms(connector->dev, "%s: EDID is invalid:\n", connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID is invalid:\n", + connector->base.id, connector->name); for (i = 0; i < num_blocks; i++) edid_block_dump(KERN_DEBUG, edid + i, i); } /* Get override or firmware EDID */ -static struct edid *drm_get_override_edid(struct drm_connector *connector, - size_t *alloc_size) +static const struct drm_edid *drm_edid_override_get(struct drm_connector *connector) { - struct edid *override = NULL; + const struct drm_edid *override = NULL; - if (connector->override_edid) - override = drm_edid_duplicate(connector->edid_blob_ptr->data); + mutex_lock(&connector->edid_override_mutex); - if (!override) - override = drm_load_edid_firmware(connector); + if (connector->edid_override) + override = drm_edid_dup(connector->edid_override); - /* FIXME: Get alloc size from deeper down the stack */ - if (!IS_ERR_OR_NULL(override) && alloc_size) - *alloc_size = edid_size(override); + mutex_unlock(&connector->edid_override_mutex); + + if (!override) + override = drm_edid_load_firmware(connector); return IS_ERR(override) ? NULL : override; } /* For debugfs edid_override implementation */ +int drm_edid_override_show(struct drm_connector *connector, struct seq_file *m) +{ + const struct drm_edid *drm_edid; + + mutex_lock(&connector->edid_override_mutex); + + drm_edid = connector->edid_override; + if (drm_edid) + seq_write(m, drm_edid->edid, drm_edid->size); + + mutex_unlock(&connector->edid_override_mutex); + + return 0; +} + +/* For debugfs edid_override implementation */ int drm_edid_override_set(struct drm_connector *connector, const void *edid, size_t size) { - int ret; + const struct drm_edid *drm_edid; - if (size < EDID_LENGTH || edid_size(edid) > size) + drm_edid = drm_edid_alloc(edid, size); + if (!drm_edid_valid(drm_edid)) { + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override invalid\n", + connector->base.id, connector->name); + drm_edid_free(drm_edid); return -EINVAL; + } - connector->override_edid = false; + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override set\n", + connector->base.id, connector->name); - ret = drm_connector_update_edid_property(connector, edid); - if (!ret) - connector->override_edid = true; + mutex_lock(&connector->edid_override_mutex); - return ret; + drm_edid_free(connector->edid_override); + connector->edid_override = drm_edid; + + mutex_unlock(&connector->edid_override_mutex); + + return 0; } /* For debugfs edid_override implementation */ int drm_edid_override_reset(struct drm_connector *connector) { - connector->override_edid = false; + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override reset\n", + connector->base.id, connector->name); + + mutex_lock(&connector->edid_override_mutex); + + drm_edid_free(connector->edid_override); + connector->edid_override = NULL; + + mutex_unlock(&connector->edid_override_mutex); - return drm_connector_update_edid_property(connector, NULL); + return 0; } /** - * drm_add_override_edid_modes - add modes from override/firmware EDID + * drm_edid_override_connector_update - add modes from override/firmware EDID * @connector: connector we're probing * * Add modes from the override/firmware EDID, if available. Only to be used from @@ -2229,24 +2307,26 @@ int drm_edid_override_reset(struct drm_connector *connector) * * Return: The number of modes added or 0 if we couldn't find any. */ -int drm_add_override_edid_modes(struct drm_connector *connector) +int drm_edid_override_connector_update(struct drm_connector *connector) { - struct edid *override; + const struct drm_edid *override; int num_modes = 0; - override = drm_get_override_edid(connector, NULL); + override = drm_edid_override_get(connector); if (override) { - drm_connector_update_edid_property(connector, override); - num_modes = drm_add_edid_modes(connector, override); - kfree(override); + if (drm_edid_connector_update(connector, override) == 0) + num_modes = drm_edid_connector_add_modes(connector); + + drm_edid_free(override); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n", - connector->base.id, connector->name, num_modes); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n", + connector->base.id, connector->name, num_modes); } return num_modes; } -EXPORT_SYMBOL(drm_add_override_edid_modes); +EXPORT_SYMBOL(drm_edid_override_connector_update); typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len); @@ -2289,12 +2369,19 @@ static struct edid *_drm_do_get_edid(struct drm_connector *connector, { enum edid_block_status status; int i, num_blocks, invalid_blocks = 0; + const struct drm_edid *override; struct edid *edid, *new; size_t alloc_size = EDID_LENGTH; - edid = drm_get_override_edid(connector, &alloc_size); - if (edid) + override = drm_edid_override_get(connector); + if (override) { + alloc_size = override->size; + edid = kmemdup(override->edid, alloc_size, GFP_KERNEL); + drm_edid_free(override); + if (!edid) + return NULL; goto ok; + } edid = kmalloc(alloc_size, GFP_KERNEL); if (!edid) @@ -2383,34 +2470,6 @@ fail: } /** - * drm_do_get_edid - get EDID data using a custom EDID block read function - * @connector: connector we're probing - * @read_block: EDID block read function - * @context: 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. - * - * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority - * order. Having either of them bypasses actual EDID reads. - * - * Return: Pointer to valid EDID or NULL if we couldn't find any. - */ -struct edid *drm_do_get_edid(struct drm_connector *connector, - read_block_fn read_block, - void *context) -{ - return _drm_do_get_edid(connector, read_block, context, NULL); -} -EXPORT_SYMBOL_GPL(drm_do_get_edid); - -/** * drm_edid_raw - Get a pointer to the raw EDID data. * @drm_edid: drm_edid container * @@ -2575,7 +2634,7 @@ EXPORT_SYMBOL(drm_get_edid); * this function. * * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority * order. Having either of them bypasses actual EDID reads. * * The returned pointer must be freed using drm_edid_free(). @@ -2613,7 +2672,7 @@ EXPORT_SYMBOL(drm_edid_read_custom); * Read EDID using the given I2C adapter. * * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority * order. Having either of them bypasses actual EDID reads. * * Prefer initializing connector->ddc with drm_connector_init_with_ddc() and @@ -2649,7 +2708,7 @@ EXPORT_SYMBOL(drm_edid_read_ddc); * Read EDID using the connector's I2C adapter. * * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority * order. Having either of them bypasses actual EDID reads. * * The returned pointer must be freed using drm_edid_free(). @@ -2665,8 +2724,84 @@ const struct drm_edid *drm_edid_read(struct drm_connector *connector) } EXPORT_SYMBOL(drm_edid_read); -static u32 edid_extract_panel_id(const struct edid *edid) +/** + * drm_edid_get_product_id - Get the vendor and product identification + * @drm_edid: EDID + * @id: Where to place the product id + */ +void drm_edid_get_product_id(const struct drm_edid *drm_edid, + struct drm_edid_product_id *id) +{ + if (drm_edid && drm_edid->edid && drm_edid->size >= EDID_LENGTH) + memcpy(id, &drm_edid->edid->product_id, sizeof(*id)); + else + memset(id, 0, sizeof(*id)); +} +EXPORT_SYMBOL(drm_edid_get_product_id); + +static void decode_date(struct seq_buf *s, const struct drm_edid_product_id *id) { + int week = id->week_of_manufacture; + int year = id->year_of_manufacture + 1990; + + if (week == 0xff) + seq_buf_printf(s, "model year: %d", year); + else if (!week) + seq_buf_printf(s, "year of manufacture: %d", year); + else + seq_buf_printf(s, "week/year of manufacture: %d/%d", week, year); +} + +/** + * drm_edid_print_product_id - Print decoded product id to printer + * @p: drm printer + * @id: EDID product id + * @raw: If true, also print the raw hex + * + * See VESA E-EDID 1.4 section 3.4. + */ +void drm_edid_print_product_id(struct drm_printer *p, + const struct drm_edid_product_id *id, bool raw) +{ + DECLARE_SEQ_BUF(date, 40); + char vend[4]; + + drm_edid_decode_mfg_id(be16_to_cpu(id->manufacturer_name), vend); + + decode_date(&date, id); + + drm_printf(p, "manufacturer name: %s, product code: %u, serial number: %u, %s\n", + vend, le16_to_cpu(id->product_code), + le32_to_cpu(id->serial_number), seq_buf_str(&date)); + + if (raw) + drm_printf(p, "raw product id: %*ph\n", (int)sizeof(*id), id); + + WARN_ON(seq_buf_has_overflowed(&date)); +} +EXPORT_SYMBOL(drm_edid_print_product_id); + +/** + * drm_edid_get_panel_id - Get a panel's ID from EDID + * @drm_edid: EDID that contains panel ID. + * + * This function uses the first block of the EDID of a panel and (assuming + * that the EDID is valid) extracts the ID out of it. The ID is a 32-bit value + * (16 bits of manufacturer ID and 16 bits of per-manufacturer ID) that's + * supposed to be different for each different modem of panel. + * + * Return: A 32-bit ID that should be different for each make/model of panel. + * See the functions drm_edid_encode_panel_id() and + * drm_edid_decode_panel_id() for some details on the structure of this + * ID. Return 0 if the EDID size is less than a base block. + */ +u32 drm_edid_get_panel_id(const struct drm_edid *drm_edid) +{ + const struct edid *edid = drm_edid->edid; + + if (drm_edid->size < EDID_LENGTH) + return 0; + /* * We represent the ID as a 32-bit number so it can easily be compared * with "==". @@ -2684,58 +2819,54 @@ static u32 edid_extract_panel_id(const struct edid *edid) (u32)edid->mfg_id[1] << 16 | (u32)EDID_PRODUCT_ID(edid); } +EXPORT_SYMBOL(drm_edid_get_panel_id); /** - * drm_edid_get_panel_id - Get a panel's ID through DDC + * drm_edid_read_base_block - Get a panel's EDID base block * @adapter: I2C adapter to use for DDC * - * This function reads the first block of the EDID of a panel and (assuming - * that the EDID is valid) extracts the ID out of it. The ID is a 32-bit value - * (16 bits of manufacturer ID and 16 bits of per-manufacturer ID) that's - * supposed to be different for each different modem of panel. + * This function returns the drm_edid containing the first block of the EDID of + * a panel. * * This function is intended to be used during early probing on devices where * more than one panel might be present. Because of its intended use it must - * assume that the EDID of the panel is correct, at least as far as the ID - * is concerned (in other words, we don't process any overrides here). + * assume that the EDID of the panel is correct, at least as far as the base + * block is concerned (in other words, we don't process any overrides here). + * + * Caller should call drm_edid_free() after use. * * NOTE: it's expected that this function and drm_do_get_edid() will both * be read the EDID, but there is no caching between them. Since we're only * reading the first block, hopefully this extra overhead won't be too big. * - * Return: A 32-bit ID that should be different for each make/model of panel. - * See the functions drm_edid_encode_panel_id() and - * drm_edid_decode_panel_id() for some details on the structure of this - * ID. + * WARNING: Only use this function when the connector is unknown. For example, + * during the early probe of panel. The EDID read from the function is temporary + * and should be replaced by the full EDID returned from other drm_edid_read. + * + * Return: Pointer to allocated EDID base block, or NULL on any failure. */ - -u32 drm_edid_get_panel_id(struct i2c_adapter *adapter) +const struct drm_edid *drm_edid_read_base_block(struct i2c_adapter *adapter) { enum edid_block_status status; void *base_block; - u32 panel_id = 0; - - /* - * There are no manufacturer IDs of 0, so if there is a problem reading - * the EDID then we'll just return 0. - */ - base_block = kmalloc(EDID_LENGTH, GFP_KERNEL); + base_block = kzalloc(EDID_LENGTH, GFP_KERNEL); if (!base_block) - return 0; + return NULL; status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter); edid_block_status_print(status, base_block, 0); - if (edid_block_status_valid(status, edid_block_tag(base_block))) - panel_id = edid_extract_panel_id(base_block); - - kfree(base_block); + if (!edid_block_status_valid(status, edid_block_tag(base_block))) { + edid_block_dump(KERN_NOTICE, base_block, 0); + kfree(base_block); + return NULL; + } - return panel_id; + return _drm_edid_alloc(base_block, EDID_LENGTH); } -EXPORT_SYMBOL(drm_edid_get_panel_id); +EXPORT_SYMBOL(drm_edid_read_base_block); /** * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output @@ -2767,6 +2898,35 @@ struct edid *drm_get_edid_switcheroo(struct drm_connector *connector, EXPORT_SYMBOL(drm_get_edid_switcheroo); /** + * drm_edid_read_switcheroo - get EDID data for a vga_switcheroo output + * @connector: connector we're probing + * @adapter: I2C adapter to use for DDC + * + * Wrapper around drm_edid_read_ddc() for laptops with dual GPUs using one set + * of outputs. The wrapper adds the requisite vga_switcheroo calls to + * temporarily switch DDC to the GPU which is retrieving EDID. + * + * Return: Pointer to valid EDID or %NULL if we couldn't find any. + */ +const struct drm_edid *drm_edid_read_switcheroo(struct drm_connector *connector, + struct i2c_adapter *adapter) +{ + struct drm_device *dev = connector->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + const struct drm_edid *drm_edid; + + if (drm_WARN_ON_ONCE(dev, !dev_is_pci(dev->dev))) + return NULL; + + vga_switcheroo_lock_ddc(pdev); + drm_edid = drm_edid_read_ddc(connector, adapter); + vga_switcheroo_unlock_ddc(pdev); + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_switcheroo); + +/** * drm_edid_duplicate - duplicate an EDID and the extensions * @edid: EDID to duplicate * @@ -2774,6 +2934,9 @@ EXPORT_SYMBOL(drm_get_edid_switcheroo); */ struct edid *drm_edid_duplicate(const struct edid *edid) { + if (!edid) + return NULL; + return kmemdup(edid, edid_size(edid), GFP_KERNEL); } EXPORT_SYMBOL(drm_edid_duplicate); @@ -2785,22 +2948,35 @@ EXPORT_SYMBOL(drm_edid_duplicate); * @drm_edid: EDID to process * * This tells subsequent routines what fixes they need to apply. + * + * Return: A u32 represents the quirks to apply. */ static u32 edid_get_quirks(const struct drm_edid *drm_edid) { - u32 panel_id = edid_extract_panel_id(drm_edid->edid); const struct edid_quirk *quirk; int i; for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { quirk = &edid_quirk_list[i]; - if (quirk->panel_id == panel_id) + if (drm_edid_match(drm_edid, &quirk->ident)) return quirk->quirks; } return 0; } +static bool drm_edid_has_internal_quirk(struct drm_connector *connector, + enum drm_edid_internal_quirk quirk) +{ + return connector->display_info.quirks & BIT(quirk); +} + +bool drm_edid_has_quirk(struct drm_connector *connector, enum drm_edid_quirk quirk) +{ + return connector->display_info.quirks & BIT(quirk); +} +EXPORT_SYMBOL(drm_edid_has_quirk); + #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t))) @@ -2808,8 +2984,7 @@ static u32 edid_get_quirks(const struct drm_edid *drm_edid) * Walk the mode list for connector, clearing the preferred status on existing * modes and setting it anew for the right mode ala quirks. */ -static void edid_fixup_preferred(struct drm_connector *connector, - u32 quirks) +static void edid_fixup_preferred(struct drm_connector *connector) { struct drm_display_mode *t, *cur_mode, *preferred_mode; int target_refresh = 0; @@ -2818,9 +2993,9 @@ static void edid_fixup_preferred(struct drm_connector *connector, if (list_empty(&connector->probed_modes)) return; - if (quirks & EDID_QUIRK_PREFER_LARGE_60) + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_PREFER_LARGE_60)) target_refresh = 60; - if (quirks & EDID_QUIRK_PREFER_LARGE_75) + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_PREFER_LARGE_75)) target_refresh = 75; preferred_mode = list_first_entry(&connector->probed_modes, @@ -2984,7 +3159,7 @@ is_rb(const struct detailed_timing *descriptor, void *data) 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) + descriptor->data.other_data.data.range.formula.cvt.flags & DRM_EDID_CVT_FLAGS_REDUCED_BLANKING) *res = true; } @@ -2999,7 +3174,7 @@ drm_monitor_supports_rb(const struct drm_edid *drm_edid) return ret; } - return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0); + return drm_edid_is_digital(drm_edid); } static void @@ -3012,7 +3187,7 @@ find_gtf2(const struct detailed_timing *descriptor, void *data) BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); - if (descriptor->data.other_data.data.range.flags == 0x02) + if (descriptor->data.other_data.data.range.flags == DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG) *res = descriptor; } @@ -3077,20 +3252,53 @@ drm_gtf2_2j(const struct drm_edid *drm_edid) return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0; } +static void +get_timing_level(const struct detailed_timing *descriptor, void *data) +{ + int *res = data; + + if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE)) + return; + + BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10); + + switch (descriptor->data.other_data.data.range.flags) { + case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG: + *res = LEVEL_GTF; + break; + case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG: + *res = LEVEL_GTF2; + break; + case DRM_EDID_CVT_SUPPORT_FLAG: + *res = LEVEL_CVT; + break; + default: + break; + } +} + /* Get standard timing level (CVT/GTF/DMT). */ static int standard_timing_level(const struct drm_edid *drm_edid) { const struct edid *edid = drm_edid->edid; - if (edid->revision >= 2) { - if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) - return LEVEL_CVT; - if (drm_gtf2_hbreak(drm_edid)) - return LEVEL_GTF2; - if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) - return LEVEL_GTF; + if (edid->revision >= 4) { + /* + * If the range descriptor doesn't + * indicate otherwise default to CVT + */ + int ret = LEVEL_CVT; + + drm_for_each_detailed_block(drm_edid, get_timing_level, &ret); + + return ret; + } else if (edid->revision >= 3 && drm_gtf2_hbreak(drm_edid)) { + return LEVEL_GTF2; + } else if (edid->revision >= 2) { + return LEVEL_GTF; + } else { + return LEVEL_DMT; } - return LEVEL_DMT; } /* @@ -3113,6 +3321,35 @@ static int drm_mode_hsync(const struct drm_display_mode *mode) return DIV_ROUND_CLOSEST(mode->clock, mode->htotal); } +static struct drm_display_mode * +drm_gtf2_mode(struct drm_device *dev, + const struct drm_edid *drm_edid, + int hsize, int vsize, int vrefresh_rate) +{ + struct drm_display_mode *mode; + + /* + * This is potentially wrong if there's ever a monitor with + * more than one ranges section, each claiming a different + * secondary GTF curve. Please don't do that. + */ + mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); + if (!mode) + return NULL; + + if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) { + drm_mode_destroy(dev, mode); + mode = drm_gtf_mode_complex(dev, hsize, vsize, + vrefresh_rate, 0, 0, + drm_gtf2_m(drm_edid), + drm_gtf2_2c(drm_edid), + drm_gtf2_k(drm_edid), + drm_gtf2_2j(drm_edid)); + } + + return mode; +} + /* * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT/GTF/DMT. @@ -3201,23 +3438,7 @@ static struct drm_display_mode *drm_mode_std(struct drm_connector *connector, mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); break; case LEVEL_GTF2: - /* - * This is potentially wrong if there's ever a monitor with - * more than one ranges section, each claiming a different - * secondary GTF curve. Please don't do that. - */ - mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); - if (!mode) - return NULL; - if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) { - drm_mode_destroy(dev, mode); - mode = drm_gtf_mode_complex(dev, hsize, vsize, - vrefresh_rate, 0, 0, - drm_gtf2_m(drm_edid), - drm_gtf2_2c(drm_edid), - drm_gtf2_k(drm_edid), - drm_gtf2_2j(drm_edid)); - } + mode = drm_gtf2_mode(dev, drm_edid, hsize, vsize, vrefresh_rate); break; case LEVEL_CVT: mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, @@ -3274,11 +3495,11 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, * timing block contains enough info for us to create and return a new struct * drm_display_mode. */ -static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, +static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector, const struct drm_edid *drm_edid, - const struct detailed_timing *timing, - u32 quirks) + const struct detailed_timing *timing) { + struct drm_device *dev = connector->dev; struct drm_display_mode *mode; const struct detailed_pixel_timing *pt = &timing->data.pixel_data; unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; @@ -3295,21 +3516,23 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return NULL; if (pt->misc & DRM_EDID_PT_STEREO) { - DRM_DEBUG_KMS("stereo mode not supported\n"); + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Stereo mode not supported\n", + connector->base.id, connector->name); return NULL; } if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { - DRM_DEBUG_KMS("composite sync not supported\n"); + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Composite sync not supported\n", + connector->base.id, connector->name); } /* it is incorrect if hsync/vsync width is zero */ if (!hsync_pulse_width || !vsync_pulse_width) { - DRM_DEBUG_KMS("Incorrect Detailed timing. " - "Wrong Hsync/Vsync pulse width\n"); + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Incorrect Detailed timing. Wrong Hsync/Vsync pulse width\n", + connector->base.id, connector->name); return NULL; } - if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_FORCE_REDUCED_BLANKING)) { mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); if (!mode) return NULL; @@ -3321,7 +3544,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, if (!mode) return NULL; - if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_135_CLOCK_TOO_HIGH)) mode->clock = 1088 * 10; else mode->clock = le16_to_cpu(timing->pixel_clock) * 10; @@ -3336,15 +3559,23 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, mode->vsync_end = mode->vsync_start + vsync_pulse_width; mode->vtotal = mode->vdisplay + vblank; - /* Some EDIDs have bogus h/vtotal values */ - if (mode->hsync_end > mode->htotal) - mode->htotal = mode->hsync_end + 1; - if (mode->vsync_end > mode->vtotal) - mode->vtotal = mode->vsync_end + 1; + /* Some EDIDs have bogus h/vsync_end values */ + if (mode->hsync_end > mode->htotal) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing hsync_end %d->%d\n", + connector->base.id, connector->name, + mode->hsync_end, mode->htotal); + mode->hsync_end = mode->htotal; + } + if (mode->vsync_end > mode->vtotal) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing vsync_end %d->%d\n", + connector->base.id, connector->name, + mode->vsync_end, mode->vtotal); + mode->vsync_end = mode->vtotal; + } drm_mode_do_interlace_quirk(mode, pt); - if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_DETAILED_SYNC_PP)) { mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; } else { mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? @@ -3357,12 +3588,12 @@ 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; - if (quirks & EDID_QUIRK_DETAILED_IN_CM) { + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_DETAILED_IN_CM)) { mode->width_mm *= 10; mode->height_mm *= 10; } - if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE)) { mode->width_mm = drm_edid->edid->width_cm * 10; mode->height_mm = drm_edid->edid->height_cm * 10; } @@ -3415,7 +3646,7 @@ range_pixel_clock(const struct edid *edid, const u8 *t) return 0; /* 1.4 with CVT support gives us real precision, yay */ - if (edid->revision >= 4 && t[10] == 0x04) + if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG) return (t[9] * 10000) - ((t[12] >> 2) * 250); /* 1.3 is pathetic, so fuzz up a bit */ @@ -3436,12 +3667,13 @@ static bool mode_in_range(const struct drm_display_mode *mode, if (!mode_in_vsync_range(mode, edid, t)) return false; - if ((max_clock = range_pixel_clock(edid, t))) + max_clock = range_pixel_clock(edid, t); + if (max_clock) if (mode->clock > max_clock) return false; /* 1.4 max horizontal check */ - if (edid->revision >= 4 && t[10] == 0x04) + if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG) if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) return false; @@ -3533,6 +3765,35 @@ static int drm_gtf_modes_for_range(struct drm_connector *connector, return modes; } +static int drm_gtf2_modes_for_range(struct drm_connector *connector, + const struct drm_edid *drm_edid, + const struct detailed_timing *timing) +{ + int i, modes = 0; + struct drm_display_mode *newmode; + struct drm_device *dev = connector->dev; + + for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { + const struct minimode *m = &extra_modes[i]; + + newmode = drm_gtf2_mode(dev, drm_edid, m->w, m->h, m->r); + if (!newmode) + return modes; + + drm_mode_fixup_1366x768(newmode); + if (!mode_in_range(newmode, drm_edid, timing) || + !valid_inferred_mode(connector, newmode)) { + drm_mode_destroy(dev, newmode); + continue; + } + + drm_mode_probed_add(connector, newmode); + modes++; + } + + return modes; +} + static int drm_cvt_modes_for_range(struct drm_connector *connector, const struct drm_edid *drm_edid, const struct detailed_timing *timing) @@ -3577,25 +3838,29 @@ do_inferred_modes(const struct detailed_timing *timing, void *c) closure->drm_edid, timing); - if (!version_greater(closure->drm_edid, 1, 1)) + if (closure->drm_edid->edid->revision < 2) return; /* GTF not defined yet */ switch (range->flags) { - case 0x02: /* secondary gtf, XXX could do more */ - case 0x00: /* default gtf */ + case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG: + closure->modes += drm_gtf2_modes_for_range(closure->connector, + closure->drm_edid, + timing); + break; + case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG: closure->modes += drm_gtf_modes_for_range(closure->connector, closure->drm_edid, timing); break; - case 0x04: /* cvt, only in 1.4+ */ - if (!version_greater(closure->drm_edid, 1, 3)) + case DRM_EDID_CVT_SUPPORT_FLAG: + if (closure->drm_edid->edid->revision < 4) break; closure->modes += drm_cvt_modes_for_range(closure->connector, closure->drm_edid, timing); break; - case 0x01: /* just the ranges, no formula */ + case DRM_EDID_RANGE_LIMITS_ONLY_FLAG: default: break; } @@ -3609,7 +3874,7 @@ static int add_inferred_modes(struct drm_connector *connector, .drm_edid = drm_edid, }; - if (version_greater(drm_edid, 1, 0)) + if (drm_edid->edid->revision >= 1) drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure); return closure.modes; @@ -3686,7 +3951,7 @@ static int add_established_modes(struct drm_connector *connector, } } - if (version_greater(drm_edid, 1, 0)) + if (edid->revision >= 1) drm_for_each_detailed_block(drm_edid, do_established_modes, &closure); @@ -3741,7 +4006,7 @@ static int add_standard_modes(struct drm_connector *connector, } } - if (version_greater(drm_edid, 1, 0)) + if (drm_edid->edid->revision >= 1) drm_for_each_detailed_block(drm_edid, do_standard_modes, &closure); @@ -3757,7 +4022,7 @@ static int drm_cvt_modes(struct drm_connector *connector, struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; const struct cvt_timing *cvt; - const int rates[] = { 60, 85, 75, 60, 50 }; + static const int rates[] = { 60, 85, 75, 60, 50 }; const u8 empty[3] = { 0, 0, 0 }; for (i = 0; i < 4; i++) { @@ -3821,7 +4086,7 @@ add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) .drm_edid = drm_edid, }; - if (version_greater(drm_edid, 1, 2)) + if (drm_edid->edid->revision >= 3) drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure); /* XXX should also look for CVT codes in VTB blocks */ @@ -3829,7 +4094,8 @@ add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) return closure.modes; } -static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); +static void fixup_detailed_cea_mode_clock(struct drm_connector *connector, + struct drm_display_mode *mode); static void do_detailed_mode(const struct detailed_timing *timing, void *c) @@ -3840,9 +4106,8 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) if (!is_detailed_timing_descriptor(timing)) return; - newmode = drm_mode_detailed(closure->connector->dev, - closure->drm_edid, timing, - closure->quirks); + newmode = drm_mode_detailed(closure->connector, + closure->drm_edid, timing); if (!newmode) return; @@ -3854,7 +4119,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) * so fix up anything that looks like CEA/HDMI mode, but the clock * is just slightly off. */ - fixup_detailed_cea_mode_clock(newmode); + fixup_detailed_cea_mode_clock(closure->connector, newmode); drm_mode_probed_add(closure->connector, newmode); closure->modes++; @@ -3865,21 +4130,20 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) * add_detailed_modes - Add modes from detailed timings * @connector: attached connector * @drm_edid: EDID block to scan - * @quirks: quirks to apply */ static int add_detailed_modes(struct drm_connector *connector, - const struct drm_edid *drm_edid, u32 quirks) + const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, .drm_edid = drm_edid, - .preferred = true, - .quirks = quirks, }; - if (closure.preferred && !version_greater(drm_edid, 1, 3)) + if (drm_edid->edid->revision >= 4) + closure.preferred = true; /* first detailed timing is always preferred */ + else closure.preferred = - (drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); + drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING; drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure); @@ -3912,7 +4176,7 @@ static int add_detailed_modes(struct drm_connector *connector, * * FIXME: Prefer not returning pointers to raw EDID data. */ -const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, +const u8 *drm_edid_find_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index) { const u8 *edid_ext = NULL; @@ -3942,11 +4206,21 @@ static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; - int ext_index = 0; + struct drm_edid_iter edid_iter; + const u8 *ext; bool found = false; /* Look for a top level CEA extension block */ - if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index)) + drm_edid_iter_begin(drm_edid, &edid_iter); + drm_edid_iter_for_each(ext, &edid_iter) { + if (ext[0] == CEA_EXT) { + found = true; + break; + } + } + drm_edid_iter_end(&edid_iter); + + if (found) return true; /* CEA blocks can also be found embedded in a DisplayID block */ @@ -4305,28 +4579,20 @@ static u8 svd_to_vic(u8 svd) return svd; } +/* + * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in + * the EDID, or NULL on errors. + */ static struct drm_display_mode * -drm_display_mode_from_vic_index(struct drm_connector *connector, - const u8 *video_db, u8 video_len, - u8 video_index) +drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index) { + const struct drm_display_info *info = &connector->display_info; struct drm_device *dev = connector->dev; - struct drm_display_mode *newmode; - u8 vic; - if (video_db == NULL || video_index >= video_len) + if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index]) return NULL; - /* CEA modes are numbered 1..127 */ - vic = svd_to_vic(video_db[video_index]); - if (!drm_valid_cea_vic(vic)) - return NULL; - - newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); - if (!newmode) - return NULL; - - return newmode; + return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]); } /* @@ -4342,10 +4608,8 @@ drm_display_mode_from_vic_index(struct drm_connector *connector, static int do_y420vdb_modes(struct drm_connector *connector, const u8 *svds, u8 svds_len) { - int modes = 0, i; struct drm_device *dev = connector->dev; - struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; + int modes = 0, i; for (i = 0; i < svds_len; i++) { u8 vic = svd_to_vic(svds[i]); @@ -4357,35 +4621,13 @@ static int do_y420vdb_modes(struct drm_connector *connector, newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); if (!newmode) break; - bitmap_set(hdmi->y420_vdb_modes, vic, 1); drm_mode_probed_add(connector, newmode); modes++; } - if (modes > 0) - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; return modes; } -/* - * drm_add_cmdb_modes - Add a YCBCR 420 mode into bitmap - * @connector: connector corresponding to the HDMI sink - * @vic: CEA vic for the video mode to be added in the map - * - * Makes an entry for a videomode in the YCBCR 420 bitmap - */ -static void -drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) -{ - u8 vic = svd_to_vic(svd); - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - - if (!drm_valid_cea_vic(vic)) - return; - - bitmap_set(hdmi->y420_cmdb_modes, vic, 1); -} - /** * drm_display_mode_from_cea_vic() - return a mode for CEA VIC * @dev: DRM device @@ -4414,29 +4656,20 @@ drm_display_mode_from_cea_vic(struct drm_device *dev, } EXPORT_SYMBOL(drm_display_mode_from_cea_vic); -static int -do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) +/* Add modes based on VICs parsed in parse_cta_vdb() */ +static int add_cta_vdb_modes(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; int i, modes = 0; - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - for (i = 0; i < len; i++) { + if (!info->vics) + return 0; + + for (i = 0; i < info->vics_len; i++) { struct drm_display_mode *mode; - mode = drm_display_mode_from_vic_index(connector, db, len, i); + mode = drm_display_mode_from_vic_index(connector, i); if (mode) { - /* - * YCBCR420 capability block contains a bitmap which - * gives the index of CEA modes from CEA VDB, which - * can support YCBCR 420 sampling output also (apart - * from RGB/YCBCR444 etc). - * For example, if the bit 0 in bitmap is set, - * first mode in VDB can support YCBCR420 output too. - * Add YCBCR420 modes only if sink is HDMI 2.0 capable. - */ - if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i)) - drm_add_cmdb_modes(connector, db[i]); - drm_mode_probed_add(connector, mode); modes++; } @@ -4515,7 +4748,8 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic) struct drm_display_mode *newmode; if (!drm_valid_hdmi_vic(vic)) { - DRM_ERROR("Unknown HDMI VIC: %d\n", vic); + drm_err(connector->dev, "[CONNECTOR:%d:%s] Unknown HDMI VIC: %d\n", + connector->base.id, connector->name, vic); return 0; } @@ -4529,15 +4763,13 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic) } static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, - const u8 *video_db, u8 video_len, u8 video_index) + int vic_index) { struct drm_display_mode *newmode; int modes = 0; if (structure & (1 << 0)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; drm_mode_probed_add(connector, newmode); @@ -4545,9 +4777,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 6)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; drm_mode_probed_add(connector, newmode); @@ -4555,9 +4785,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 8)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; drm_mode_probed_add(connector, newmode); @@ -4568,6 +4796,26 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, return modes; } +static bool hdmi_vsdb_latency_present(const u8 *db) +{ + return db[8] & BIT(7); +} + +static bool hdmi_vsdb_i_latency_present(const u8 *db) +{ + return hdmi_vsdb_latency_present(db) && db[8] & BIT(6); +} + +static int hdmi_vsdb_latency_length(const u8 *db) +{ + if (hdmi_vsdb_i_latency_present(db)) + return 4; + else if (hdmi_vsdb_latency_present(db)) + return 2; + else + return 0; +} + /* * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block * @connector: connector corresponding to the HDMI sink @@ -4578,10 +4826,8 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, * also adds the stereo 3d modes when applicable. */ static int -do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, - const u8 *video_db, u8 video_len) +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) { - struct drm_display_info *info = &connector->display_info; int modes = 0, offset = 0, i, multi_present = 0, multi_len; u8 vic_len, hdmi_3d_len = 0; u16 mask; @@ -4594,13 +4840,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, 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; + offset += hdmi_vsdb_latency_length(db); /* the declared length is not long enough for the 2 first bytes * of additional video format capabilities */ @@ -4654,9 +4894,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, for (i = 0; i < 16; i++) { if (mask & (1 << i)) modes += add_3d_struct_modes(connector, - structure_all, - video_db, - video_len, i); + structure_all, i); } } @@ -4693,8 +4931,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, if (newflag != 0) { newmode = drm_display_mode_from_vic_index(connector, - video_db, - video_len, vic_index); if (newmode) { @@ -4709,8 +4945,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, } out: - if (modes > 0) - info->has_hdmi_infoframe = true; return modes; } @@ -5040,20 +5274,26 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid) return cta[4 + 2]; } -static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, - const u8 *db) +/* + * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB) + * + * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB, + * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444 + * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can + * support YCBCR420 output too. + */ +static void parse_cta_y420cmdb(struct drm_connector *connector, + const struct cea_db *db, u64 *y420cmdb_map) { struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; - u8 map_len = cea_db_payload_len(db) - 1; - u8 count; + int i, map_len = cea_db_payload_len(db) - 1; + const u8 *data = cea_db_data(db) + 1; u64 map = 0; if (map_len == 0) { /* All CEA modes support ycbcr420 sampling also.*/ - hdmi->y420_cmdb_map = U64_MAX; - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - return; + map = U64_MAX; + goto out; } /* @@ -5071,13 +5311,14 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, if (WARN_ON_ONCE(map_len > 8)) map_len = 8; - for (count = 0; count < map_len; count++) - map |= (u64)db[2 + count] << (8 * count); + for (i = 0; i < map_len; i++) + map |= (u64)data[i] << (8 * i); +out: if (map) info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - hdmi->y420_cmdb_map = map; + *y420cmdb_map = map; } static int add_cea_modes(struct drm_connector *connector, @@ -5085,21 +5326,16 @@ static int add_cea_modes(struct drm_connector *connector, { const struct cea_db *db; struct cea_db_iter iter; - int modes = 0; + int modes; + + /* CTA VDB block VICs parsed earlier */ + modes = add_cta_vdb_modes(connector); cea_db_iter_edid_begin(drm_edid, &iter); cea_db_iter_for_each(db, &iter) { - const u8 *hdmi = NULL, *video = NULL; - u8 hdmi_len = 0, video_len = 0; - - if (cea_db_tag(db) == CTA_DB_VIDEO) { - video = cea_db_data(db); - video_len = cea_db_payload_len(db); - modes += do_cea_modes(connector, video, video_len); - } else if (cea_db_is_hdmi_vsdb(db)) { - /* FIXME: Switch to use cea_db_data() */ - hdmi = (const u8 *)db; - hdmi_len = cea_db_payload_len(db); + if (cea_db_is_hdmi_vsdb(db)) { + modes += do_hdmi_vsdb_modes(connector, (const u8 *)db, + cea_db_payload_len(db)); } else if (cea_db_is_y420vdb(db)) { const u8 *vdb420 = cea_db_data(db) + 1; @@ -5107,22 +5343,14 @@ static int add_cea_modes(struct drm_connector *connector, modes += do_y420vdb_modes(connector, vdb420, cea_db_payload_len(db) - 1); } - - /* - * We parse the HDMI VSDB after having added the cea modes as we - * will be patching their flags when the sink supports stereo - * 3D. - */ - if (hdmi) - modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, - video, video_len); } cea_db_iter_end(&iter); return modes; } -static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) +static void fixup_detailed_cea_mode_clock(struct drm_connector *connector, + struct drm_display_mode *mode) { const struct drm_display_mode *cea_mode; int clock1, clock2, clock; @@ -5160,14 +5388,17 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) if (mode->clock == clock) return; - DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n", - type, vic, mode->clock, clock); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] detailed mode matches %s VIC %d, adjusting clock %d -> %d\n", + connector->base.id, connector->name, + type, vic, mode->clock, clock); mode->clock = clock; } static void drm_calculate_luminance_range(struct drm_connector *connector) { - struct hdr_static_metadata *hdr_metadata = &connector->hdr_sink_metadata.hdmi_type1; + const struct hdr_static_metadata *hdr_metadata = + &connector->display_info.hdr_sink_metadata.hdmi_type1; struct drm_luminance_range_info *luminance_range = &connector->display_info.luminance_range; static const u8 pre_computed_values[] = { @@ -5228,27 +5459,28 @@ static uint8_t hdr_metadata_type(const u8 *edid_ext) static void drm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db) { + struct hdr_static_metadata *hdr_metadata = + &connector->display_info.hdr_sink_metadata.hdmi_type1; u16 len; len = cea_db_payload_len(db); - connector->hdr_sink_metadata.hdmi_type1.eotf = - eotf_supported(db); - connector->hdr_sink_metadata.hdmi_type1.metadata_type = - hdr_metadata_type(db); + hdr_metadata->eotf = eotf_supported(db); + hdr_metadata->metadata_type = hdr_metadata_type(db); if (len >= 4) - connector->hdr_sink_metadata.hdmi_type1.max_cll = db[4]; + hdr_metadata->max_cll = db[4]; if (len >= 5) - connector->hdr_sink_metadata.hdmi_type1.max_fall = db[5]; + hdr_metadata->max_fall = db[5]; if (len >= 6) { - connector->hdr_sink_metadata.hdmi_type1.min_cll = db[6]; + hdr_metadata->min_cll = db[6]; /* Calculate only when all values are available */ drm_calculate_luminance_range(connector); } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) { @@ -5256,30 +5488,87 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) if (len >= 6 && (db[6] & (1 << 7))) connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI; - if (len >= 8) { - connector->latency_present[0] = db[8] >> 7; - connector->latency_present[1] = (db[8] >> 6) & 1; - } - if (len >= 9) + + if (len >= 10 && hdmi_vsdb_latency_present(db)) { + connector->latency_present[0] = true; connector->video_latency[0] = db[9]; - if (len >= 10) connector->audio_latency[0] = db[10]; - if (len >= 11) + } + + if (len >= 12 && hdmi_vsdb_i_latency_present(db)) { + connector->latency_present[1] = true; connector->video_latency[1] = db[11]; - if (len >= 12) connector->audio_latency[1] = db[12]; + } + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n", + connector->base.id, connector->name, + connector->latency_present[0], connector->latency_present[1], + connector->video_latency[0], connector->video_latency[1], + connector->audio_latency[0], connector->audio_latency[1]); +} + +static void +match_identity(const struct detailed_timing *timing, void *data) +{ + struct drm_edid_match_closure *closure = data; + unsigned int i; + const char *name = closure->ident->name; + unsigned int name_len = strlen(name); + const char *desc = timing->data.other_data.data.str.str; + unsigned int desc_len = ARRAY_SIZE(timing->data.other_data.data.str.str); + + if (name_len > desc_len || + !(is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME) || + is_display_descriptor(timing, EDID_DETAIL_MONITOR_STRING))) + return; + + if (strncmp(name, desc, name_len)) + return; + + for (i = name_len; i < desc_len; i++) { + if (desc[i] == '\n') + break; + /* Allow white space before EDID string terminator. */ + if (!isspace(desc[i])) + return; + } - DRM_DEBUG_KMS("HDMI: latency present %d %d, " - "video latency %d %d, " - "audio latency %d %d\n", - connector->latency_present[0], - connector->latency_present[1], - connector->video_latency[0], - connector->video_latency[1], - connector->audio_latency[0], - connector->audio_latency[1]); + closure->matched = true; } +/** + * drm_edid_match - match drm_edid with given identity + * @drm_edid: EDID + * @ident: the EDID identity to match with + * + * Check if the EDID matches with the given identity. + * + * Return: True if the given identity matched with EDID, false otherwise. + */ +bool drm_edid_match(const struct drm_edid *drm_edid, + const struct drm_edid_ident *ident) +{ + if (!drm_edid || drm_edid_get_panel_id(drm_edid) != ident->panel_id) + return false; + + /* Match with name only if it's not NULL. */ + if (ident->name) { + struct drm_edid_match_closure closure = { + .ident = ident, + .matched = false, + }; + + drm_for_each_detailed_block(drm_edid, match_identity, &closure); + + return closure.matched; + } + + return true; +} +EXPORT_SYMBOL(drm_edid_match); + static void monitor_name(const struct detailed_timing *timing, void *data) { @@ -5341,7 +5630,9 @@ EXPORT_SYMBOL(drm_edid_get_monitor_name); static void clear_eld(struct drm_connector *connector) { + mutex_lock(&connector->eld_mutex); memset(connector->eld, 0, sizeof(connector->eld)); + mutex_unlock(&connector->eld_mutex); connector->latency_present[0] = false; connector->latency_present[1] = false; @@ -5352,6 +5643,27 @@ static void clear_eld(struct drm_connector *connector) } /* + * Get 3-byte SAD buffer from struct cea_sad. + */ +void drm_edid_cta_sad_get(const struct cea_sad *cta_sad, u8 *sad) +{ + sad[0] = cta_sad->format << 3 | cta_sad->channels; + sad[1] = cta_sad->freq; + sad[2] = cta_sad->byte2; +} + +/* + * Set struct cea_sad from 3-byte SAD buffer. + */ +void drm_edid_cta_sad_set(struct cea_sad *cta_sad, const u8 *sad) +{ + cta_sad->format = (sad[0] & 0x78) >> 3; + cta_sad->channels = sad[0] & 0x07; + cta_sad->freq = sad[1] & 0x7f; + cta_sad->byte2 = sad[2]; +} + +/* * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink * @drm_edid: EDID to parse @@ -5369,13 +5681,15 @@ static void drm_edid_to_eld(struct drm_connector *connector, int total_sad_count = 0; int mnl; - clear_eld(connector); - if (!drm_edid) return; + mutex_lock(&connector->eld_mutex); + mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]); - DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD monitor %s\n", + connector->base.id, connector->name, + &eld[DRM_ELD_MONITOR_NAME_STRING]); eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT; eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl; @@ -5429,12 +5743,15 @@ static void drm_edid_to_eld(struct drm_connector *connector, 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), total_sad_count); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD size %d, SAD count %d\n", + connector->base.id, connector->name, + drm_eld_size(eld), total_sad_count); + + mutex_unlock(&connector->eld_mutex); } static int _drm_edid_to_sad(const struct drm_edid *drm_edid, - struct cea_sad **sads) + struct cea_sad **psads) { const struct cea_db *db; struct cea_db_iter iter; @@ -5443,20 +5760,16 @@ static int _drm_edid_to_sad(const struct drm_edid *drm_edid, cea_db_iter_edid_begin(drm_edid, &iter); cea_db_iter_for_each(db, &iter) { if (cea_db_tag(db) == CTA_DB_AUDIO) { - int j; + struct cea_sad *sads; + int i; count = cea_db_payload_len(db) / 3; /* SAD is 3B */ - *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); - if (!*sads) + sads = kcalloc(count, sizeof(*sads), GFP_KERNEL); + *psads = sads; + if (!sads) return -ENOMEM; - for (j = 0; j < count; j++) { - const u8 *sad = &db->data[j * 3]; - - (*sads)[j].format = (sad[0] & 0x78) >> 3; - (*sads)[j].channels = sad[0] & 0x7; - (*sads)[j].freq = sad[1] & 0x7F; - (*sads)[j].byte2 = sad[2]; - } + for (i = 0; i < count; i++) + drm_edid_cta_sad_set(&sads[i], &db->data[i * 3]); break; } } @@ -5697,11 +6010,98 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +/* CTA-861 Video Data Block (CTA VDB) */ +static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + int i, vic_index, len = cea_db_payload_len(db); + const u8 *svds = cea_db_data(db); + u8 *vics; + + if (!len) + return; + + /* Gracefully handle multiple VDBs, however unlikely that is */ + vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL); + if (!vics) + return; + + vic_index = info->vics_len; + info->vics_len += len; + info->vics = vics; + + for (i = 0; i < len; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + vic = 0; + + info->vics[vic_index++] = vic; + } +} + +/* + * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB. + * + * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed + * using the VICs themselves. + */ +static void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map)); + + for (i = 0; i < len; i++) { + u8 vic = info->vics[i]; + + if (vic && y420cmdb_map & BIT_ULL(i)) + bitmap_set(hdmi->y420_cmdb_modes, vic, 1); + } +} + +static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic) +{ + const struct drm_display_info *info = &connector->display_info; + int i; + + if (!vic || !info->vics) + return false; + + for (i = 0; i < info->vics_len; i++) { + if (info->vics[i] == vic) + return true; + } + + return false; +} + +/* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */ +static void parse_cta_y420vdb(struct drm_connector *connector, + const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + const u8 *svds = cea_db_data(db) + 1; + int i; + + for (i = 0; i < cea_db_payload_len(db) - 1; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + continue; + + bitmap_set(hdmi->y420_vdb_modes, vic, 1); + info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; + } +} + static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db) { struct drm_display_info *info = &connector->display_info; - DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", db[2]); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] CEA VCDB 0x%02x\n", + connector->base.id, connector->name, db[2]); if (db[2] & EDID_CEA_VCDB_QS) info->rgb_quant_range_selectable = true; @@ -5752,14 +6152,89 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector, hdmi->y420_dc_modes = dc_mask; } +static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, + const u8 *hf_scds) +{ + hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2; + + if (!hdmi_dsc->v_1p2) + return; + + hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420; + hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP; + + if (hf_scds[11] & DRM_EDID_DSC_16BPC) + hdmi_dsc->bpc_supported = 16; + else if (hf_scds[11] & DRM_EDID_DSC_12BPC) + hdmi_dsc->bpc_supported = 12; + else if (hf_scds[11] & DRM_EDID_DSC_10BPC) + hdmi_dsc->bpc_supported = 10; + else + /* Supports min 8 BPC if DSC 1.2 is supported*/ + hdmi_dsc->bpc_supported = 8; + + if (cea_db_payload_len(hf_scds) >= 12 && hf_scds[12]) { + u8 dsc_max_slices; + u8 dsc_max_frl_rate; + + dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4; + drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes, + &hdmi_dsc->max_frl_rate_per_lane); + + dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES; + + switch (dsc_max_slices) { + case 1: + hdmi_dsc->max_slices = 1; + hdmi_dsc->clk_per_slice = 340; + break; + case 2: + hdmi_dsc->max_slices = 2; + hdmi_dsc->clk_per_slice = 340; + break; + case 3: + hdmi_dsc->max_slices = 4; + hdmi_dsc->clk_per_slice = 340; + break; + case 4: + hdmi_dsc->max_slices = 8; + hdmi_dsc->clk_per_slice = 340; + break; + case 5: + hdmi_dsc->max_slices = 8; + hdmi_dsc->clk_per_slice = 400; + break; + case 6: + hdmi_dsc->max_slices = 12; + hdmi_dsc->clk_per_slice = 400; + break; + case 7: + hdmi_dsc->max_slices = 16; + hdmi_dsc->clk_per_slice = 400; + break; + case 0: + default: + hdmi_dsc->max_slices = 0; + hdmi_dsc->clk_per_slice = 0; + } + } + + if (cea_db_payload_len(hf_scds) >= 13 && hf_scds[13]) + hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES; +} + /* Sink Capability Data Structure */ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, const u8 *hf_scds) { - struct drm_display_info *display = &connector->display_info; - struct drm_hdmi_info *hdmi = &display->hdmi; + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap; + int max_tmds_clock = 0; + u8 max_frl_rate = 0; + bool dsc_support = false; - display->has_hdmi_infoframe = true; + info->has_hdmi_infoframe = true; if (hf_scds[6] & 0x80) { hdmi->scdc.supported = true; @@ -5777,14 +6252,13 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, */ if (hf_scds[5]) { - /* max clock is 5000 KHz times block value */ - u32 max_tmds_clock = hf_scds[5] * 5000; struct drm_scdc *scdc = &hdmi->scdc; + /* max clock is 5000 KHz times block value */ + max_tmds_clock = hf_scds[5] * 5000; + if (max_tmds_clock > 340000) { - display->max_tmds_clock = max_tmds_clock; - DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n", - display->max_tmds_clock); + info->max_tmds_clock = max_tmds_clock; } if (scdc->supported) { @@ -5797,74 +6271,22 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, } if (hf_scds[7]) { - u8 max_frl_rate; - u8 dsc_max_frl_rate; - u8 dsc_max_slices; - struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap; - - DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4; drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes, &hdmi->max_frl_rate_per_lane); - hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2; - - if (hdmi_dsc->v_1p2) { - hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420; - hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP; - - if (hf_scds[11] & DRM_EDID_DSC_16BPC) - hdmi_dsc->bpc_supported = 16; - else if (hf_scds[11] & DRM_EDID_DSC_12BPC) - hdmi_dsc->bpc_supported = 12; - else if (hf_scds[11] & DRM_EDID_DSC_10BPC) - hdmi_dsc->bpc_supported = 10; - else - hdmi_dsc->bpc_supported = 0; - - dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4; - drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes, - &hdmi_dsc->max_frl_rate_per_lane); - hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES; - - dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES; - switch (dsc_max_slices) { - case 1: - hdmi_dsc->max_slices = 1; - hdmi_dsc->clk_per_slice = 340; - break; - case 2: - hdmi_dsc->max_slices = 2; - hdmi_dsc->clk_per_slice = 340; - break; - case 3: - hdmi_dsc->max_slices = 4; - hdmi_dsc->clk_per_slice = 340; - break; - case 4: - hdmi_dsc->max_slices = 8; - hdmi_dsc->clk_per_slice = 340; - break; - case 5: - hdmi_dsc->max_slices = 8; - hdmi_dsc->clk_per_slice = 400; - break; - case 6: - hdmi_dsc->max_slices = 12; - hdmi_dsc->clk_per_slice = 400; - break; - case 7: - hdmi_dsc->max_slices = 16; - hdmi_dsc->clk_per_slice = 400; - break; - case 0: - default: - hdmi_dsc->max_slices = 0; - hdmi_dsc->clk_per_slice = 0; - } - } } drm_parse_ycbcr420_deep_color_info(connector, hf_scds); + + if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) { + drm_parse_dsc_info(hdmi_dsc, hf_scds); + dsc_support = true; + } + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n", + connector->base.id, connector->name, + max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support)); } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, @@ -5882,39 +6304,39 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, if (hdmi[6] & DRM_EDID_HDMI_DC_30) { dc_bpc = 10; info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_30; - DRM_DEBUG("%s: HDMI sink does deep color 30.\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 30.\n", + connector->base.id, connector->name); } if (hdmi[6] & DRM_EDID_HDMI_DC_36) { dc_bpc = 12; info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_36; - DRM_DEBUG("%s: HDMI sink does deep color 36.\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 36.\n", + connector->base.id, connector->name); } if (hdmi[6] & DRM_EDID_HDMI_DC_48) { dc_bpc = 16; info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_48; - DRM_DEBUG("%s: HDMI sink does deep color 48.\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 48.\n", + connector->base.id, connector->name); } if (dc_bpc == 0) { - DRM_DEBUG("%s: No deep color support on this HDMI sink.\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] No deep color support on this HDMI sink.\n", + connector->base.id, connector->name); return; } - DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n", - connector->name, dc_bpc); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Assigning HDMI sink color depth as %d bpc.\n", + connector->base.id, connector->name, dc_bpc); info->bpc = dc_bpc; /* YCRCB444 is optional according to spec. */ if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) { info->edid_hdmi_ycbcr444_dc_modes = info->edid_hdmi_rgb444_dc_modes; - DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does YCRCB444 in deep color.\n", + connector->base.id, connector->name); } /* @@ -5922,11 +6344,12 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, * then deep color 36 bit must be supported. */ if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) { - DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n", - connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink should do DC_36, but does not!\n", + connector->base.id, connector->name); } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) { @@ -5935,15 +6358,25 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) info->is_hdmi = true; + info->source_physical_address = (db[4] << 8) | db[5]; + if (len >= 6) info->dvi_dual = db[6] & 1; if (len >= 7) info->max_tmds_clock = db[7] * 5000; - DRM_DEBUG_KMS("HDMI: DVI dual %d, " - "max TMDS clock %d kHz\n", - info->dvi_dual, - info->max_tmds_clock); + /* + * Try to infer whether the sink supports HDMI infoframes. + * + * HDMI infoframe support was first added in HDMI 1.4. Assume the sink + * supports infoframes if HDMI_Video_present is set. + */ + if (len >= 8 && db[8] & BIT(5)) + info->has_hdmi_infoframe = true; + + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n", + connector->base.id, connector->name, + info->dvi_dual, info->max_tmds_clock); drm_parse_hdmi_deep_color_info(connector, db); } @@ -5963,8 +6396,9 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector, if (version == 1 || version == 2 || (version == 3 && !desktop_usage)) info->non_desktop = true; - drm_dbg_kms(connector->dev, "HMD or specialized display VSDB version %u: 0x%02x\n", - version, db[5]); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] HMD or specialized display VSDB version %u: 0x%02x\n", + connector->base.id, connector->name, version, db[5]); } static void drm_parse_cea_ext(struct drm_connector *connector, @@ -5975,6 +6409,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, const struct cea_db *db; struct cea_db_iter iter; const u8 *edid_ext; + u64 y420cmdb_map = 0; drm_edid_iter_begin(drm_edid, &edid_iter); drm_edid_iter_for_each(edid_ext, &edid_iter) { @@ -5985,8 +6420,10 @@ static void drm_parse_cea_ext(struct drm_connector *connector, info->cea_rev = edid_ext[1]; if (info->cea_rev != edid_ext[1]) - DRM_DEBUG_KMS("CEA extension version mismatch %u != %u\n", - info->cea_rev, edid_ext[1]); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] CEA extension version mismatch %u != %u\n", + connector->base.id, connector->name, + info->cea_rev, edid_ext[1]); /* The existence of a CTA extension should imply RGB support */ info->color_formats = DRM_COLOR_FORMAT_RGB444; @@ -5994,6 +6431,9 @@ static void drm_parse_cea_ext(struct drm_connector *connector, info->color_formats |= DRM_COLOR_FORMAT_YCBCR444; if (edid_ext[3] & EDID_CEA_YCRCB422) info->color_formats |= DRM_COLOR_FORMAT_YCBCR422; + if (edid_ext[3] & EDID_BASIC_AUDIO) + info->has_audio = true; + } drm_edid_iter_end(&edid_iter); @@ -6010,13 +6450,22 @@ static void drm_parse_cea_ext(struct drm_connector *connector, else if (cea_db_is_microsoft_vsdb(db)) drm_parse_microsoft_vsdb(connector, data); else if (cea_db_is_y420cmdb(db)) - drm_parse_y420cmdb_bitmap(connector, data); + parse_cta_y420cmdb(connector, db, &y420cmdb_map); + else if (cea_db_is_y420vdb(db)) + parse_cta_y420vdb(connector, db); else if (cea_db_is_vcdb(db)) drm_parse_vcdb(connector, data); else if (cea_db_is_hdmi_hdr_metadata_block(db)) drm_parse_hdr_metadata_block(connector, data); + else if (cea_db_tag(db) == CTA_DB_VIDEO) + parse_cta_vdb(connector, db); + else if (cea_db_tag(db) == CTA_DB_AUDIO) + info->has_audio = true; } cea_db_iter_end(&iter); + + if (y420cmdb_map) + update_cta_y420cmdb(connector, y420cmdb_map); } static @@ -6033,10 +6482,13 @@ void get_monitor_range(const struct detailed_timing *timing, void *c) return; /* - * Check for flag range limits only. If flag == 1 then - * no additional timing information provided. - * Default GTF, GTF Secondary curve and CVT are not - * supported + * These limits are used to determine the VRR refresh + * rate range. Only the "range limits only" variant + * of the range descriptor seems to guarantee that + * any and all timings are accepted by the sink, as + * opposed to just timings conforming to the indicated + * formula (GTF/GTF2/CVT). Thus other variants of the + * range descriptor are not accepted here. */ if (range->flags != DRM_EDID_RANGE_LIMITS_ONLY_FLAG) return; @@ -6061,14 +6513,18 @@ static void drm_get_monitor_range(struct drm_connector *connector, .drm_edid = drm_edid, }; - if (!version_greater(drm_edid, 1, 1)) + if (drm_edid->edid->revision < 4) + return; + + if (!(drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ)) return; drm_for_each_detailed_block(drm_edid, get_monitor_range, &closure); - DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n", - info->monitor_range.min_vfreq, - info->monitor_range.max_vfreq); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Supported Monitor Refresh rate range is %d Hz - %d Hz\n", + connector->base.id, connector->name, + info->monitor_range.min_vfreq, info->monitor_range.max_vfreq); } static void drm_parse_vesa_mso_data(struct drm_connector *connector, @@ -6079,8 +6535,9 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector, struct drm_display_info *info = &connector->display_info; if (block->num_bytes < 3) { - drm_dbg_kms(connector->dev, "Unexpected vendor block size %u\n", - block->num_bytes); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected vendor block size %u\n", + connector->base.id, connector->name, block->num_bytes); return; } @@ -6088,13 +6545,16 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector, return; if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) { - drm_dbg_kms(connector->dev, "Unexpected VESA vendor block size\n"); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected VESA vendor block size\n", + connector->base.id, connector->name); return; } switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) { default: - drm_dbg_kms(connector->dev, "Reserved MSO mode value\n"); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved MSO mode value\n", + connector->base.id, connector->name); fallthrough; case 0: info->mso_stream_count = 0; @@ -6114,12 +6574,16 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector, info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso); if (info->mso_pixel_overlap > 8) { - drm_dbg_kms(connector->dev, "Reserved MSO pixel overlap value %u\n", + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n", + connector->base.id, connector->name, info->mso_pixel_overlap); info->mso_pixel_overlap = 8; } - drm_dbg_kms(connector->dev, "MSO stream count %u, pixel overlap %u\n", + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n", + connector->base.id, connector->name, info->mso_stream_count, info->mso_pixel_overlap); } @@ -6153,9 +6617,11 @@ static void drm_reset_display_info(struct drm_connector *connector) info->max_tmds_clock = 0; info->dvi_dual = false; info->is_hdmi = false; + info->has_audio = false; info->has_hdmi_infoframe = false; info->rgb_quant_range_selectable = false; memset(&info->hdmi, 0, sizeof(info->hdmi)); + memset(&info->hdr_sink_metadata, 0, sizeof(info->hdr_sink_metadata)); info->edid_hdmi_rgb444_dc_modes = 0; info->edid_hdmi_ycbcr444_dc_modes = 0; @@ -6166,17 +6632,60 @@ static void drm_reset_display_info(struct drm_connector *connector) info->mso_stream_count = 0; info->mso_pixel_overlap = 0; + info->max_dsc_bpp = 0; + + kfree(info->vics); + info->vics = NULL; + info->vics_len = 0; + + info->quirks = 0; + + info->source_physical_address = CEC_PHYS_ADDR_INVALID; } -static u32 update_display_info(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static void update_displayid_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; - const struct edid *edid = drm_edid->edid; + const struct displayid_block *block; + struct displayid_iter iter; - u32 quirks = edid_get_quirks(drm_edid); + displayid_iter_edid_begin(drm_edid, &iter); + displayid_iter_for_each(block, &iter) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID extension version 0x%02x, primary use 0x%02x\n", + connector->base.id, connector->name, + displayid_version(&iter), + displayid_primary_use(&iter)); + if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 && + (displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_VR || + displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_AR)) + info->non_desktop = true; + + /* + * We're only interested in the base section here, no need to + * iterate further. + */ + break; + } + displayid_iter_end(&iter); +} + +static void update_display_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + struct drm_display_info *info = &connector->display_info; + const struct edid *edid; drm_reset_display_info(connector); + clear_eld(connector); + + if (!drm_edid) + return; + + edid = drm_edid->edid; + + info->quirks = edid_get_quirks(drm_edid); info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -6186,12 +6695,14 @@ static u32 update_display_info(struct drm_connector *connector, if (edid->revision < 3) goto out; - if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) + if (!drm_edid_is_digital(drm_edid)) goto out; info->color_formats |= DRM_COLOR_FORMAT_RGB444; drm_parse_cea_ext(connector, drm_edid); + update_displayid_info(connector, drm_edid); + /* * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3? * @@ -6202,8 +6713,9 @@ static u32 update_display_info(struct drm_connector *connector, if (info->bpc == 0 && edid->revision == 3 && edid->input & DRM_EDID_DIGITAL_DFP_1_X) { info->bpc = 8; - DRM_DEBUG("%s: Assigning DFP sink color depth as %d bpc.\n", - connector->name, info->bpc); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Assigning DFP sink color depth as %d bpc.\n", + connector->base.id, connector->name, info->bpc); } /* Only defined for 1.4 with digital displays */ @@ -6235,8 +6747,9 @@ static u32 update_display_info(struct drm_connector *connector, break; } - DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n", - connector->name, info->bpc); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Assigning EDID-1.4 digital sink color depth as %d bpc.\n", + connector->base.id, connector->name, info->bpc); if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444) info->color_formats |= DRM_COLOR_FORMAT_YCBCR444; @@ -6246,33 +6759,50 @@ static u32 update_display_info(struct drm_connector *connector, drm_update_mso(connector, drm_edid); out: - if (quirks & EDID_QUIRK_NON_DESKTOP) { - drm_dbg_kms(connector->dev, "Non-desktop display%s\n", + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_NON_DESKTOP)) { + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n", + connector->base.id, connector->name, info->non_desktop ? " (redundant quirk)" : ""); info->non_desktop = true; } - return quirks; + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_CAP_DSC_15BPP)) + info->max_dsc_bpp = 15; + + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_FORCE_6BPC)) + info->bpc = 6; + + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_FORCE_8BPC)) + info->bpc = 8; + + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_FORCE_10BPC)) + info->bpc = 10; + + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_FORCE_12BPC)) + info->bpc = 12; + + /* Depends on info->cea_rev set by drm_parse_cea_ext() above */ + drm_edid_to_eld(connector, drm_edid); } static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, - struct displayid_detailed_timings_1 *timings, + const struct displayid_detailed_timings_1 *timings, bool type_7) { struct drm_display_mode *mode; - unsigned pixel_clock = (timings->pixel_clock[0] | - (timings->pixel_clock[1] << 8) | - (timings->pixel_clock[2] << 16)) + 1; - unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1; - unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1; - unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1; - unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1; - unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1; - unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1; - unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1; - unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1; - bool hsync_positive = (timings->hsync[1] >> 7) & 0x1; - bool vsync_positive = (timings->vsync[1] >> 7) & 0x1; + unsigned int pixel_clock = (timings->pixel_clock[0] | + (timings->pixel_clock[1] << 8) | + (timings->pixel_clock[2] << 16)) + 1; + unsigned int hactive = le16_to_cpu(timings->hactive) + 1; + unsigned int hblank = le16_to_cpu(timings->hblank) + 1; + unsigned int hsync = (le16_to_cpu(timings->hsync) & 0x7fff) + 1; + unsigned int hsync_width = le16_to_cpu(timings->hsw) + 1; + unsigned int vactive = le16_to_cpu(timings->vactive) + 1; + unsigned int vblank = le16_to_cpu(timings->vblank) + 1; + unsigned int vsync = (le16_to_cpu(timings->vsync) & 0x7fff) + 1; + unsigned int vsync_width = le16_to_cpu(timings->vsw) + 1; + bool hsync_positive = le16_to_cpu(timings->hsync) & (1 << 15); + bool vsync_positive = le16_to_cpu(timings->vsync) & (1 << 15); mode = drm_mode_create(dev); if (!mode) @@ -6329,6 +6859,66 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, return num_modes; } +static struct drm_display_mode *drm_mode_displayid_formula(struct drm_device *dev, + const struct displayid_formula_timings_9 *timings, + bool type_10) +{ + struct drm_display_mode *mode; + u16 hactive = le16_to_cpu(timings->hactive) + 1; + u16 vactive = le16_to_cpu(timings->vactive) + 1; + u8 timing_formula = timings->flags & 0x7; + + /* TODO: support RB-v2 & RB-v3 */ + if (timing_formula > 1) + return NULL; + + /* TODO: support video-optimized refresh rate */ + if (timings->flags & (1 << 4)) + drm_dbg_kms(dev, "Fractional vrefresh is not implemented, proceeding with non-video-optimized refresh rate"); + + mode = drm_cvt_mode(dev, hactive, vactive, timings->vrefresh + 1, timing_formula == 1, false, false); + if (!mode) + return NULL; + + /* TODO: interpret S3D flags */ + + mode->type = DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(mode); + + return mode; +} + +static int add_displayid_formula_modes(struct drm_connector *connector, + const struct displayid_block *block) +{ + const struct displayid_formula_timing_block *formula_block = (struct displayid_formula_timing_block *)block; + int num_timings; + struct drm_display_mode *newmode; + int num_modes = 0; + bool type_10 = block->tag == DATA_BLOCK_2_TYPE_10_FORMULA_TIMING; + int timing_size = 6 + ((formula_block->base.rev & 0x70) >> 4); + + /* extended blocks are not supported yet */ + if (timing_size != 6) + return 0; + + if (block->num_bytes % timing_size) + return 0; + + num_timings = block->num_bytes / timing_size; + for (int i = 0; i < num_timings; i++) { + const struct displayid_formula_timings_9 *timings = &formula_block->timings[i]; + + newmode = drm_mode_displayid_formula(connector->dev, timings, type_10); + if (!newmode) + continue; + + drm_mode_probed_add(connector, newmode); + num_modes++; + } + return num_modes; +} + static int add_displayid_detailed_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6341,33 +6931,22 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING || block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING) num_modes += add_displayid_detailed_1_modes(connector, block); + else if (block->tag == DATA_BLOCK_2_TYPE_9_FORMULA_TIMING || + block->tag == DATA_BLOCK_2_TYPE_10_FORMULA_TIMING) + num_modes += add_displayid_formula_modes(connector, block); } displayid_iter_end(&iter); return num_modes; } -static int _drm_edid_connector_update(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static int _drm_edid_connector_add_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { int num_modes = 0; - u32 quirks; - if (!drm_edid) { - drm_reset_display_info(connector); - clear_eld(connector); + if (!drm_edid) return 0; - } - - /* - * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. - * To avoid multiple parsing of same block, lets parse that map - * from sink info, before parsing CEA modes. - */ - quirks = update_display_info(connector, drm_edid); - - /* Depends on info->cea_rev set by update_display_info() above */ - drm_edid_to_eld(connector, drm_edid); /* * EDID spec says modes should be preferred in this order: @@ -6383,30 +6962,19 @@ static int _drm_edid_connector_update(struct drm_connector *connector, * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_modes(connector, drm_edid, quirks); + num_modes += add_detailed_modes(connector, drm_edid); num_modes += add_cvt_modes(connector, drm_edid); num_modes += add_standard_modes(connector, drm_edid); num_modes += add_established_modes(connector, drm_edid); num_modes += add_cea_modes(connector, drm_edid); num_modes += add_alternate_cea_modes(connector, drm_edid); num_modes += add_displayid_detailed_modes(connector, drm_edid); - if (drm_edid->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) + if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ) num_modes += add_inferred_modes(connector, drm_edid); - if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) - edid_fixup_preferred(connector, quirks); - - if (quirks & EDID_QUIRK_FORCE_6BPC) - connector->display_info.bpc = 6; - - if (quirks & EDID_QUIRK_FORCE_8BPC) - connector->display_info.bpc = 8; - - if (quirks & EDID_QUIRK_FORCE_10BPC) - connector->display_info.bpc = 10; - - if (quirks & EDID_QUIRK_FORCE_12BPC) - connector->display_info.bpc = 12; + if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_PREFER_LARGE_60) || + drm_edid_has_internal_quirk(connector, EDID_QUIRK_PREFER_LARGE_75)) + edid_fixup_preferred(connector); return num_modes; } @@ -6421,15 +6989,14 @@ static int _drm_edid_connector_property_update(struct drm_connector *connector, int ret; if (connector->edid_blob_ptr) { - const struct edid *old_edid = connector->edid_blob_ptr->data; - - if (old_edid) { - if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) { - connector->epoch_counter++; - drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n", - connector->base.id, connector->name, - connector->epoch_counter); - } + const void *old_edid = connector->edid_blob_ptr->data; + size_t old_edid_size = connector->edid_blob_ptr->length; + + if (old_edid && !drm_edid_eq(drm_edid, old_edid, old_edid_size)) { + connector->epoch_counter++; + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n", + connector->base.id, connector->name, + connector->epoch_counter); } } @@ -6465,75 +7032,92 @@ out: return ret; } +/* For sysfs edid show implementation */ +ssize_t drm_edid_connector_property_show(struct drm_connector *connector, + char *buf, loff_t off, size_t count) +{ + const void *edid; + size_t size; + ssize_t ret = 0; + + mutex_lock(&connector->dev->mode_config.mutex); + + if (!connector->edid_blob_ptr) + goto unlock; + + edid = connector->edid_blob_ptr->data; + size = connector->edid_blob_ptr->length; + if (!edid) + goto unlock; + + if (off >= size) + goto unlock; + + if (off + count > size) + count = size - off; + + memcpy(buf, edid + off, count); + + ret = count; +unlock: + mutex_unlock(&connector->dev->mode_config.mutex); + + return ret; +} + /** * drm_edid_connector_update - Update connector information from EDID * @connector: Connector * @drm_edid: EDID * - * Update the connector mode list, display info, ELD, HDR metadata, relevant - * properties, etc. from the passed in EDID. + * Update the connector display info, ELD, HDR metadata, relevant properties, + * etc. from the passed in EDID. * * If EDID is NULL, reset the information. * - * Return: The number of modes added or 0 if we couldn't find any. + * Must be called before calling drm_edid_connector_add_modes(). + * + * Return: 0 on success, negative error on errors. */ int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { - int count; - - /* - * FIXME: Reconcile the differences in override_edid handling between - * this and drm_connector_update_edid_property(). - * - * If override_edid is set, and the EDID passed in here originates from - * drm_edid_read() and friends, it will be the override EDID, and there - * are no issues. drm_connector_update_edid_property() ignoring requests - * to set the EDID dates back to a time when override EDID was not - * handled at the low level EDID read. - * - * The only way the EDID passed in here can be different from the - * override EDID is when a driver passes in an EDID that does *not* - * originate from drm_edid_read() and friends, or passes in a stale - * cached version. This, in turn, is a question of when an override EDID - * set via debugfs should take effect. - */ - - count = _drm_edid_connector_update(connector, drm_edid); + update_display_info(connector, drm_edid); _drm_update_tile_info(connector, drm_edid); - /* Note: Ignore errors for now. */ - _drm_edid_connector_property_update(connector, drm_edid); - - return count; + return _drm_edid_connector_property_update(connector, drm_edid); } EXPORT_SYMBOL(drm_edid_connector_update); -static int _drm_connector_update_edid_property(struct drm_connector *connector, - const struct drm_edid *drm_edid) +/** + * drm_edid_connector_add_modes - Update probed modes from the EDID property + * @connector: Connector + * + * Add the modes from the previously updated EDID property to the connector + * probed modes list. + * + * drm_edid_connector_update() must have been called before this to update the + * EDID property. + * + * Return: The number of modes added, or 0 if we couldn't find any. + */ +int drm_edid_connector_add_modes(struct drm_connector *connector) { - /* ignore requests to set edid when overridden */ - if (connector->override_edid) - return 0; + const struct drm_edid *drm_edid = NULL; + int count; - /* - * Set the display info, using edid if available, otherwise resetting - * the values to defaults. This duplicates the work done in - * drm_add_edid_modes, but that function is not consistently called - * before this one in all drivers and the computation is cheap enough - * that it seems better to duplicate it rather than attempt to ensure - * some arbitrary ordering of calls. - */ - if (drm_edid) - update_display_info(connector, drm_edid); - else - drm_reset_display_info(connector); + if (connector->edid_blob_ptr) + drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data, + connector->edid_blob_ptr->length); - _drm_update_tile_info(connector, drm_edid); + count = _drm_edid_connector_add_modes(connector, drm_edid); - return _drm_edid_connector_property_update(connector, drm_edid); + drm_edid_free(drm_edid); + + return count; } +EXPORT_SYMBOL(drm_edid_connector_add_modes); /** * drm_connector_update_edid_property - update the edid property of a connector @@ -6556,8 +7140,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector, { struct drm_edid drm_edid; - return _drm_connector_update_edid_property(connector, - drm_edid_legacy_init(&drm_edid, edid)); + return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_connector_update_edid_property); @@ -6570,22 +7153,26 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); * &drm_display_info structure and ELD in @connector with any information which * can be derived from the edid. * - * This function is deprecated. Use drm_edid_connector_update() instead. + * This function is deprecated. Use drm_edid_connector_add_modes() instead. * * 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) { - struct drm_edid drm_edid; + struct drm_edid _drm_edid; + const struct drm_edid *drm_edid; if (edid && !drm_edid_is_valid(edid)) { - drm_warn(connector->dev, "%s: EDID invalid.\n", - connector->name); + drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n", + connector->base.id, connector->name); edid = NULL; } - return _drm_edid_connector_update(connector, - drm_edid_legacy_init(&drm_edid, edid)); + drm_edid = drm_edid_legacy_init(&_drm_edid, edid); + + update_display_info(connector, drm_edid); + + return _drm_edid_connector_add_modes(connector, drm_edid); } EXPORT_SYMBOL(drm_add_edid_modes); @@ -6601,18 +7188,12 @@ EXPORT_SYMBOL(drm_add_edid_modes); * Return: The number of modes added or 0 if we couldn't find any. */ int drm_add_modes_noedid(struct drm_connector *connector, - int hdisplay, int vdisplay) + unsigned int hdisplay, unsigned int vdisplay) { - int i, count, num_modes = 0; + int i, count = ARRAY_SIZE(drm_dmt_modes), num_modes = 0; struct drm_display_mode *mode; struct drm_device *dev = connector->dev; - count = ARRAY_SIZE(drm_dmt_modes); - if (hdisplay < 0) - hdisplay = 0; - if (vdisplay < 0) - vdisplay = 0; - for (i = 0; i < count; i++) { const struct drm_display_mode *ptr = &drm_dmt_modes[i]; @@ -6638,28 +7219,6 @@ int drm_add_modes_noedid(struct drm_connector *connector, } EXPORT_SYMBOL(drm_add_modes_noedid); -/** - * drm_set_preferred_mode - Sets the preferred mode of a connector - * @connector: connector whose mode list should be processed - * @hpref: horizontal resolution of preferred mode - * @vpref: vertical resolution of preferred mode - * - * Marks a mode as preferred if it matches the resolution specified by @hpref - * and @vpref. - */ -void drm_set_preferred_mode(struct drm_connector *connector, - int hpref, int vpref) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &connector->probed_modes, head) { - if (mode->hdisplay == hpref && - mode->vdisplay == vpref) - mode->type |= DRM_MODE_TYPE_PREFERRED; - } -} -EXPORT_SYMBOL(drm_set_preferred_mode); - static bool is_hdmi2_sink(const struct drm_connector *connector) { /* @@ -6692,8 +7251,6 @@ static u8 drm_mode_hdmi_vic(const struct drm_connector *connector, static u8 drm_mode_cea_vic(const struct drm_connector *connector, const struct drm_display_mode *mode) { - u8 vic; - /* * HDMI spec says if a mode is found in HDMI 1.4b 4K modes * we should send its VIC in vendor infoframes, else send the @@ -6703,14 +7260,23 @@ static u8 drm_mode_cea_vic(const struct drm_connector *connector, if (drm_mode_hdmi_vic(connector, mode)) return 0; - vic = drm_match_cea_mode(mode); + return drm_match_cea_mode(mode); +} - /* - * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but - * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we - * have to make sure we dont break HDMI 1.4 sinks. - */ - if (!is_hdmi2_sink(connector) && vic > 64) +/* + * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that + * conform to HDMI 1.4. + * + * HDMI 1.4 (CTA-861-D) VIC range: [1..64] + * HDMI 2.0 (CTA-861-F) VIC range: [1..107] + * + * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI + * version. + */ +static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic) +{ + if (!is_hdmi2_sink(connector) && vic > 64 && + !cta_vdb_has_vic(connector, vic)) return 0; return vic; @@ -6785,7 +7351,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, picture_aspect = HDMI_PICTURE_ASPECT_NONE; } - frame->video_code = vic; + frame->video_code = vic_for_avi_infoframe(connector, vic); frame->picture_aspect = picture_aspect; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; @@ -6837,7 +7403,7 @@ drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, * by non-zero YQ when receiving RGB. There doesn't seem to be any * good way to tell which version of CEA-861 the sink supports, so * we limit non-zero YQ to HDMI 2.0 sinks only as HDMI 2.0 is based - * on on CEA-861-F. + * on CEA-861-F. */ if (!is_hdmi2_sink(connector) || rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) @@ -6956,11 +7522,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector, 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]); + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] tile cap 0x%x, size %dx%d, num tiles %dx%d, location %dx%d, vend %c%c%c", + connector->base.id, connector->name, + tile->tile_cap, + connector->tile_h_size, connector->tile_v_size, + connector->num_h_tile, connector->num_v_tile, + connector->tile_h_loc, connector->tile_v_loc, + 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) @@ -6980,6 +7549,15 @@ static void drm_parse_tiled_block(struct drm_connector *connector, } } +static bool displayid_is_tiled_block(const struct displayid_iter *iter, + const struct displayid_block *block) +{ + return (displayid_version(iter) < DISPLAY_ID_STRUCTURE_VER_20 && + block->tag == DATA_BLOCK_TILED_DISPLAY) || + (displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 && + block->tag == DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY); +} + static void _drm_update_tile_info(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6990,7 +7568,7 @@ static void _drm_update_tile_info(struct drm_connector *connector, displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { - if (block->tag == DATA_BLOCK_TILED_DISPLAY) + if (displayid_is_tiled_block(&iter, block)) drm_parse_tiled_block(connector, block); } displayid_iter_end(&iter); @@ -7000,3 +7578,16 @@ static void _drm_update_tile_info(struct drm_connector *connector, connector->tile_group = NULL; } } + +/** + * drm_edid_is_digital - is digital? + * @drm_edid: The EDID + * + * Return true if input is digital. + */ +bool drm_edid_is_digital(const struct drm_edid *drm_edid) +{ + return drm_edid && drm_edid->edid && + drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL; +} +EXPORT_SYMBOL(drm_edid_is_digital); |
