15718399fSFrançois Tigeot /* 25718399fSFrançois Tigeot * Copyright (c) 2006 Luc Verhaegen (quirks list) 35718399fSFrançois Tigeot * Copyright (c) 2007-2008 Intel Corporation 45718399fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 55718399fSFrançois Tigeot * Copyright 2010 Red Hat, Inc. 65718399fSFrançois Tigeot * 75718399fSFrançois Tigeot * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from 85718399fSFrançois Tigeot * FB layer. 95718399fSFrançois Tigeot * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> 105718399fSFrançois Tigeot * 115718399fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 125718399fSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 135718399fSFrançois Tigeot * to deal in the Software without restriction, including without limitation 145718399fSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sub license, 155718399fSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 165718399fSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 175718399fSFrançois Tigeot * 185718399fSFrançois Tigeot * The above copyright notice and this permission notice (including the 195718399fSFrançois Tigeot * next paragraph) shall be included in all copies or substantial portions 205718399fSFrançois Tigeot * of the Software. 215718399fSFrançois Tigeot * 225718399fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 235718399fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 245718399fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 255718399fSFrançois Tigeot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 265718399fSFrançois Tigeot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 275718399fSFrançois Tigeot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 285718399fSFrançois Tigeot * DEALINGS IN THE SOFTWARE. 295718399fSFrançois Tigeot */ 306e29dde8SFrançois Tigeot #include <linux/kernel.h> 3187cc1051SMichael Neumann #include <linux/hdmi.h> 326e29dde8SFrançois Tigeot #include <linux/i2c.h> 33d82bf20eSFrançois Tigeot #include <linux/module.h> 34*b8e997a6SFrançois Tigeot #include <linux/vga_switcheroo.h> 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 3618e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 378621f407SFrançois Tigeot #include <drm/drm_displayid.h> 389edbd4a0SFrançois Tigeot #include <linux/string.h> 39d82bf20eSFrançois Tigeot 405718399fSFrançois Tigeot #include <bus/iicbus/iic.h> 415718399fSFrançois Tigeot #include <bus/iicbus/iiconf.h> 425718399fSFrançois Tigeot #include "iicbus_if.h" 435718399fSFrançois Tigeot 445718399fSFrançois Tigeot #define version_greater(edid, maj, min) \ 455718399fSFrançois Tigeot (((edid)->version > (maj)) || \ 465718399fSFrançois Tigeot ((edid)->version == (maj) && (edid)->revision > (min))) 475718399fSFrançois Tigeot 485718399fSFrançois Tigeot #define EDID_EST_TIMINGS 16 495718399fSFrançois Tigeot #define EDID_STD_TIMINGS 8 505718399fSFrançois Tigeot #define EDID_DETAILED_TIMINGS 4 515718399fSFrançois Tigeot 525718399fSFrançois Tigeot /* 535718399fSFrançois Tigeot * EDID blocks out in the wild have a variety of bugs, try to collect 545718399fSFrançois Tigeot * them here (note that userspace may work around broken monitors first, 555718399fSFrançois Tigeot * but fixes should make their way here so that the kernel "just works" 565718399fSFrançois Tigeot * on as many displays as possible). 575718399fSFrançois Tigeot */ 585718399fSFrançois Tigeot 595718399fSFrançois Tigeot /* First detailed mode wrong, use largest 60Hz mode */ 605718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) 615718399fSFrançois Tigeot /* Reported 135MHz pixel clock is too high, needs adjustment */ 625718399fSFrançois Tigeot #define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) 635718399fSFrançois Tigeot /* Prefer the largest mode at 75 Hz */ 645718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) 655718399fSFrançois Tigeot /* Detail timing is in cm not mm */ 665718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_IN_CM (1 << 3) 675718399fSFrançois Tigeot /* Detailed timing descriptors have bogus size values, so just take the 685718399fSFrançois Tigeot * maximum size and use that. 695718399fSFrançois Tigeot */ 705718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) 715718399fSFrançois Tigeot /* Monitor forgot to set the first detailed is preferred bit. */ 725718399fSFrançois Tigeot #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) 735718399fSFrançois Tigeot /* use +hsync +vsync for detailed mode */ 745718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) 756e29dde8SFrançois Tigeot /* Force reduced-blanking timings for detailed modes */ 766e29dde8SFrançois Tigeot #define EDID_QUIRK_FORCE_REDUCED_BLANKING (1 << 7) 779edbd4a0SFrançois Tigeot /* Force 8bpc */ 789edbd4a0SFrançois Tigeot #define EDID_QUIRK_FORCE_8BPC (1 << 8) 79ba55f2f5SFrançois Tigeot /* Force 12bpc */ 80ba55f2f5SFrançois Tigeot #define EDID_QUIRK_FORCE_12BPC (1 << 9) 818621f407SFrançois Tigeot /* Force 6bpc */ 828621f407SFrançois Tigeot #define EDID_QUIRK_FORCE_6BPC (1 << 10) 835718399fSFrançois Tigeot 845718399fSFrançois Tigeot struct detailed_mode_closure { 855718399fSFrançois Tigeot struct drm_connector *connector; 865718399fSFrançois Tigeot struct edid *edid; 875718399fSFrançois Tigeot bool preferred; 885718399fSFrançois Tigeot u32 quirks; 895718399fSFrançois Tigeot int modes; 905718399fSFrançois Tigeot }; 915718399fSFrançois Tigeot 925718399fSFrançois Tigeot #define LEVEL_DMT 0 935718399fSFrançois Tigeot #define LEVEL_GTF 1 945718399fSFrançois Tigeot #define LEVEL_GTF2 2 955718399fSFrançois Tigeot #define LEVEL_CVT 3 965718399fSFrançois Tigeot 975718399fSFrançois Tigeot static struct edid_quirk { 986e29dde8SFrançois Tigeot char vendor[4]; 995718399fSFrançois Tigeot int product_id; 1005718399fSFrançois Tigeot u32 quirks; 1015718399fSFrançois Tigeot } edid_quirk_list[] = { 1025718399fSFrançois Tigeot /* Acer AL1706 */ 1035718399fSFrançois Tigeot { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, 1045718399fSFrançois Tigeot /* Acer F51 */ 1055718399fSFrançois Tigeot { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, 1065718399fSFrançois Tigeot /* Unknown Acer */ 1075718399fSFrançois Tigeot { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1085718399fSFrançois Tigeot 1098621f407SFrançois Tigeot /* AEO model 0 reports 8 bpc, but is a 6 bpc panel */ 1108621f407SFrançois Tigeot { "AEO", 0, EDID_QUIRK_FORCE_6BPC }, 1118621f407SFrançois Tigeot 1125718399fSFrançois Tigeot /* Belinea 10 15 55 */ 1135718399fSFrançois Tigeot { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, 1145718399fSFrançois Tigeot { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, 1155718399fSFrançois Tigeot 1165718399fSFrançois Tigeot /* Envision Peripherals, Inc. EN-7100e */ 1175718399fSFrançois Tigeot { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, 1185718399fSFrançois Tigeot /* Envision EN2028 */ 1195718399fSFrançois Tigeot { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 }, 1205718399fSFrançois Tigeot 1215718399fSFrançois Tigeot /* Funai Electronics PM36B */ 1225718399fSFrançois Tigeot { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | 1235718399fSFrançois Tigeot EDID_QUIRK_DETAILED_IN_CM }, 1245718399fSFrançois Tigeot 1255718399fSFrançois Tigeot /* LG Philips LCD LP154W01-A5 */ 1265718399fSFrançois Tigeot { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1275718399fSFrançois Tigeot { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1285718399fSFrançois Tigeot 1295718399fSFrançois Tigeot /* Philips 107p5 CRT */ 1305718399fSFrançois Tigeot { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1315718399fSFrançois Tigeot 1325718399fSFrançois Tigeot /* Proview AY765C */ 1335718399fSFrançois Tigeot { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1345718399fSFrançois Tigeot 1355718399fSFrançois Tigeot /* Samsung SyncMaster 205BW. Note: irony */ 1365718399fSFrançois Tigeot { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, 1375718399fSFrançois Tigeot /* Samsung SyncMaster 22[5-6]BW */ 1385718399fSFrançois Tigeot { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, 1395718399fSFrançois Tigeot { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, 1406e29dde8SFrançois Tigeot 141ba55f2f5SFrançois Tigeot /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */ 142ba55f2f5SFrançois Tigeot { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC }, 143ba55f2f5SFrançois Tigeot 1446e29dde8SFrançois Tigeot /* ViewSonic VA2026w */ 1456e29dde8SFrançois Tigeot { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, 1469edbd4a0SFrançois Tigeot 1479edbd4a0SFrançois Tigeot /* Medion MD 30217 PG */ 1489edbd4a0SFrançois Tigeot { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, 1499edbd4a0SFrançois Tigeot 1509edbd4a0SFrançois Tigeot /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ 1519edbd4a0SFrançois Tigeot { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, 1525718399fSFrançois Tigeot }; 1535718399fSFrançois Tigeot 154d82bf20eSFrançois Tigeot /* 155d82bf20eSFrançois Tigeot * Autogenerated from the DMT spec. 156d82bf20eSFrançois Tigeot * This table is copied from xfree86/modes/xf86EdidModes.c. 157d82bf20eSFrançois Tigeot */ 158d82bf20eSFrançois Tigeot static const struct drm_display_mode drm_dmt_modes[] = { 15919c468b4SFrançois Tigeot /* 0x01 - 640x350@85Hz */ 160d82bf20eSFrançois Tigeot { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 161d82bf20eSFrançois Tigeot 736, 832, 0, 350, 382, 385, 445, 0, 162d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 16319c468b4SFrançois Tigeot /* 0x02 - 640x400@85Hz */ 164d82bf20eSFrançois Tigeot { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 165d82bf20eSFrançois Tigeot 736, 832, 0, 400, 401, 404, 445, 0, 166d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 16719c468b4SFrançois Tigeot /* 0x03 - 720x400@85Hz */ 168d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, 169d82bf20eSFrançois Tigeot 828, 936, 0, 400, 401, 404, 446, 0, 170d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 17119c468b4SFrançois Tigeot /* 0x04 - 640x480@60Hz */ 172d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 17319c468b4SFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 174d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 17519c468b4SFrançois Tigeot /* 0x05 - 640x480@72Hz */ 176d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 177d82bf20eSFrançois Tigeot 704, 832, 0, 480, 489, 492, 520, 0, 178d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 17919c468b4SFrançois Tigeot /* 0x06 - 640x480@75Hz */ 180d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 181d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 182d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 18319c468b4SFrançois Tigeot /* 0x07 - 640x480@85Hz */ 184d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, 185d82bf20eSFrançois Tigeot 752, 832, 0, 480, 481, 484, 509, 0, 186d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 18719c468b4SFrançois Tigeot /* 0x08 - 800x600@56Hz */ 188d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 189d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 190d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 19119c468b4SFrançois Tigeot /* 0x09 - 800x600@60Hz */ 192d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 193d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 194d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 19519c468b4SFrançois Tigeot /* 0x0a - 800x600@72Hz */ 196d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 197d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 198d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 19919c468b4SFrançois Tigeot /* 0x0b - 800x600@75Hz */ 200d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 201d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 202d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20319c468b4SFrançois Tigeot /* 0x0c - 800x600@85Hz */ 204d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, 205d82bf20eSFrançois Tigeot 896, 1048, 0, 600, 601, 604, 631, 0, 206d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 20719c468b4SFrançois Tigeot /* 0x0d - 800x600@120Hz RB */ 208d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848, 209d82bf20eSFrançois Tigeot 880, 960, 0, 600, 603, 607, 636, 0, 210d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 21119c468b4SFrançois Tigeot /* 0x0e - 848x480@60Hz */ 212d82bf20eSFrançois Tigeot { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, 213d82bf20eSFrançois Tigeot 976, 1088, 0, 480, 486, 494, 517, 0, 214d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 21519c468b4SFrançois Tigeot /* 0x0f - 1024x768@43Hz, interlace */ 216d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, 217c0e85e96SFrançois Tigeot 1208, 1264, 0, 768, 768, 776, 817, 0, 218d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 219d82bf20eSFrançois Tigeot DRM_MODE_FLAG_INTERLACE) }, 22019c468b4SFrançois Tigeot /* 0x10 - 1024x768@60Hz */ 221d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 222d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 223d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 22419c468b4SFrançois Tigeot /* 0x11 - 1024x768@70Hz */ 225d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 226d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 227d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 22819c468b4SFrançois Tigeot /* 0x12 - 1024x768@75Hz */ 229d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, 230d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 231d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 23219c468b4SFrançois Tigeot /* 0x13 - 1024x768@85Hz */ 233d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, 234d82bf20eSFrançois Tigeot 1168, 1376, 0, 768, 769, 772, 808, 0, 235d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 23619c468b4SFrançois Tigeot /* 0x14 - 1024x768@120Hz RB */ 237d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072, 238d82bf20eSFrançois Tigeot 1104, 1184, 0, 768, 771, 775, 813, 0, 239d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 24019c468b4SFrançois Tigeot /* 0x15 - 1152x864@75Hz */ 241d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 242d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 243d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 24419c468b4SFrançois Tigeot /* 0x55 - 1280x720@60Hz */ 24519c468b4SFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 24619c468b4SFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 24719c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 24819c468b4SFrançois Tigeot /* 0x16 - 1280x768@60Hz RB */ 249d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328, 250d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 790, 0, 251d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 25219c468b4SFrançois Tigeot /* 0x17 - 1280x768@60Hz */ 253d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, 254d82bf20eSFrançois Tigeot 1472, 1664, 0, 768, 771, 778, 798, 0, 255d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 25619c468b4SFrançois Tigeot /* 0x18 - 1280x768@75Hz */ 257d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, 258d82bf20eSFrançois Tigeot 1488, 1696, 0, 768, 771, 778, 805, 0, 25919c468b4SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 26019c468b4SFrançois Tigeot /* 0x19 - 1280x768@85Hz */ 261d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, 262d82bf20eSFrançois Tigeot 1496, 1712, 0, 768, 771, 778, 809, 0, 263d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 26419c468b4SFrançois Tigeot /* 0x1a - 1280x768@120Hz RB */ 265d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328, 266d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 813, 0, 267d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 26819c468b4SFrançois Tigeot /* 0x1b - 1280x800@60Hz RB */ 269d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328, 270d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 823, 0, 271d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 27219c468b4SFrançois Tigeot /* 0x1c - 1280x800@60Hz */ 273d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, 274d82bf20eSFrançois Tigeot 1480, 1680, 0, 800, 803, 809, 831, 0, 27519c468b4SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 27619c468b4SFrançois Tigeot /* 0x1d - 1280x800@75Hz */ 277d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, 278d82bf20eSFrançois Tigeot 1488, 1696, 0, 800, 803, 809, 838, 0, 279d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 28019c468b4SFrançois Tigeot /* 0x1e - 1280x800@85Hz */ 281d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, 282d82bf20eSFrançois Tigeot 1496, 1712, 0, 800, 803, 809, 843, 0, 283d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 28419c468b4SFrançois Tigeot /* 0x1f - 1280x800@120Hz RB */ 285d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328, 286d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 847, 0, 287d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 28819c468b4SFrançois Tigeot /* 0x20 - 1280x960@60Hz */ 289d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, 290d82bf20eSFrançois Tigeot 1488, 1800, 0, 960, 961, 964, 1000, 0, 291d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 29219c468b4SFrançois Tigeot /* 0x21 - 1280x960@85Hz */ 293d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, 294d82bf20eSFrançois Tigeot 1504, 1728, 0, 960, 961, 964, 1011, 0, 295d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 29619c468b4SFrançois Tigeot /* 0x22 - 1280x960@120Hz RB */ 297d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328, 298d82bf20eSFrançois Tigeot 1360, 1440, 0, 960, 963, 967, 1017, 0, 299d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 30019c468b4SFrançois Tigeot /* 0x23 - 1280x1024@60Hz */ 301d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, 302d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 303d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 30419c468b4SFrançois Tigeot /* 0x24 - 1280x1024@75Hz */ 305d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 306d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 307d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 30819c468b4SFrançois Tigeot /* 0x25 - 1280x1024@85Hz */ 309d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, 310d82bf20eSFrançois Tigeot 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, 311d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 31219c468b4SFrançois Tigeot /* 0x26 - 1280x1024@120Hz RB */ 313d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328, 314d82bf20eSFrançois Tigeot 1360, 1440, 0, 1024, 1027, 1034, 1084, 0, 315d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 31619c468b4SFrançois Tigeot /* 0x27 - 1360x768@60Hz */ 317d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, 318d82bf20eSFrançois Tigeot 1536, 1792, 0, 768, 771, 777, 795, 0, 319d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 32019c468b4SFrançois Tigeot /* 0x28 - 1360x768@120Hz RB */ 321d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408, 322d82bf20eSFrançois Tigeot 1440, 1520, 0, 768, 771, 776, 813, 0, 323d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 32419c468b4SFrançois Tigeot /* 0x51 - 1366x768@60Hz */ 32519c468b4SFrançois Tigeot { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 85500, 1366, 1436, 32619c468b4SFrançois Tigeot 1579, 1792, 0, 768, 771, 774, 798, 0, 32719c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 32819c468b4SFrançois Tigeot /* 0x56 - 1366x768@60Hz */ 32919c468b4SFrançois Tigeot { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 72000, 1366, 1380, 33019c468b4SFrançois Tigeot 1436, 1500, 0, 768, 769, 772, 800, 0, 33119c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 33219c468b4SFrançois Tigeot /* 0x29 - 1400x1050@60Hz RB */ 333d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448, 334d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1080, 0, 335d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 33619c468b4SFrançois Tigeot /* 0x2a - 1400x1050@60Hz */ 337d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, 338d82bf20eSFrançois Tigeot 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 339d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 34019c468b4SFrançois Tigeot /* 0x2b - 1400x1050@75Hz */ 341d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, 342d82bf20eSFrançois Tigeot 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, 343d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 34419c468b4SFrançois Tigeot /* 0x2c - 1400x1050@85Hz */ 345d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, 346d82bf20eSFrançois Tigeot 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, 347d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 34819c468b4SFrançois Tigeot /* 0x2d - 1400x1050@120Hz RB */ 349d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448, 350d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1112, 0, 351d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 35219c468b4SFrançois Tigeot /* 0x2e - 1440x900@60Hz RB */ 353d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, 354d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 926, 0, 355d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 35619c468b4SFrançois Tigeot /* 0x2f - 1440x900@60Hz */ 357d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, 358d82bf20eSFrançois Tigeot 1672, 1904, 0, 900, 903, 909, 934, 0, 359d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 36019c468b4SFrançois Tigeot /* 0x30 - 1440x900@75Hz */ 361d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, 362d82bf20eSFrançois Tigeot 1688, 1936, 0, 900, 903, 909, 942, 0, 363d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 36419c468b4SFrançois Tigeot /* 0x31 - 1440x900@85Hz */ 365d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, 366d82bf20eSFrançois Tigeot 1696, 1952, 0, 900, 903, 909, 948, 0, 367d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 36819c468b4SFrançois Tigeot /* 0x32 - 1440x900@120Hz RB */ 369d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488, 370d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 953, 0, 371d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 37219c468b4SFrançois Tigeot /* 0x53 - 1600x900@60Hz */ 37319c468b4SFrançois Tigeot { DRM_MODE("1600x900", DRM_MODE_TYPE_DRIVER, 108000, 1600, 1624, 37419c468b4SFrançois Tigeot 1704, 1800, 0, 900, 901, 904, 1000, 0, 37519c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 37619c468b4SFrançois Tigeot /* 0x33 - 1600x1200@60Hz */ 377d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, 378d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 379d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 38019c468b4SFrançois Tigeot /* 0x34 - 1600x1200@65Hz */ 381d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, 382d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 383d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 38419c468b4SFrançois Tigeot /* 0x35 - 1600x1200@70Hz */ 385d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, 386d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 387d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 38819c468b4SFrançois Tigeot /* 0x36 - 1600x1200@75Hz */ 389d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, 390d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 391d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 39219c468b4SFrançois Tigeot /* 0x37 - 1600x1200@85Hz */ 393d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, 394d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 395d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 39619c468b4SFrançois Tigeot /* 0x38 - 1600x1200@120Hz RB */ 397d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648, 398d82bf20eSFrançois Tigeot 1680, 1760, 0, 1200, 1203, 1207, 1271, 0, 399d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 40019c468b4SFrançois Tigeot /* 0x39 - 1680x1050@60Hz RB */ 401d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728, 402d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1080, 0, 403d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 40419c468b4SFrançois Tigeot /* 0x3a - 1680x1050@60Hz */ 405d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, 406d82bf20eSFrançois Tigeot 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 407d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 40819c468b4SFrançois Tigeot /* 0x3b - 1680x1050@75Hz */ 409d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, 410d82bf20eSFrançois Tigeot 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, 411d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 41219c468b4SFrançois Tigeot /* 0x3c - 1680x1050@85Hz */ 413d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, 414d82bf20eSFrançois Tigeot 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, 415d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 41619c468b4SFrançois Tigeot /* 0x3d - 1680x1050@120Hz RB */ 417d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728, 418d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1112, 0, 419d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 42019c468b4SFrançois Tigeot /* 0x3e - 1792x1344@60Hz */ 421d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, 422d82bf20eSFrançois Tigeot 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, 423d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 42419c468b4SFrançois Tigeot /* 0x3f - 1792x1344@75Hz */ 425d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, 426d82bf20eSFrançois Tigeot 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, 427d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 42819c468b4SFrançois Tigeot /* 0x40 - 1792x1344@120Hz RB */ 429d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840, 430d82bf20eSFrançois Tigeot 1872, 1952, 0, 1344, 1347, 1351, 1423, 0, 431d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 43219c468b4SFrançois Tigeot /* 0x41 - 1856x1392@60Hz */ 433d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, 434d82bf20eSFrançois Tigeot 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, 435d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 43619c468b4SFrançois Tigeot /* 0x42 - 1856x1392@75Hz */ 437d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, 43819c468b4SFrançois Tigeot 2208, 2560, 0, 1392, 1393, 1396, 1500, 0, 439d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 44019c468b4SFrançois Tigeot /* 0x43 - 1856x1392@120Hz RB */ 441d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904, 442d82bf20eSFrançois Tigeot 1936, 2016, 0, 1392, 1395, 1399, 1474, 0, 443d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 44419c468b4SFrançois Tigeot /* 0x52 - 1920x1080@60Hz */ 44519c468b4SFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 44619c468b4SFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 44719c468b4SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 44819c468b4SFrançois Tigeot /* 0x44 - 1920x1200@60Hz RB */ 449d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968, 450d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1235, 0, 451d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 45219c468b4SFrançois Tigeot /* 0x45 - 1920x1200@60Hz */ 453d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, 454d82bf20eSFrançois Tigeot 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 455d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 45619c468b4SFrançois Tigeot /* 0x46 - 1920x1200@75Hz */ 457d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, 458d82bf20eSFrançois Tigeot 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, 459d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 46019c468b4SFrançois Tigeot /* 0x47 - 1920x1200@85Hz */ 461d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, 462d82bf20eSFrançois Tigeot 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, 463d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 46419c468b4SFrançois Tigeot /* 0x48 - 1920x1200@120Hz RB */ 465d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968, 466d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1271, 0, 467d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 46819c468b4SFrançois Tigeot /* 0x49 - 1920x1440@60Hz */ 469d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, 470d82bf20eSFrançois Tigeot 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, 471d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 47219c468b4SFrançois Tigeot /* 0x4a - 1920x1440@75Hz */ 473d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, 474d82bf20eSFrançois Tigeot 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, 475d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 47619c468b4SFrançois Tigeot /* 0x4b - 1920x1440@120Hz RB */ 477d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968, 478d82bf20eSFrançois Tigeot 2000, 2080, 0, 1440, 1443, 1447, 1525, 0, 479d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 48019c468b4SFrançois Tigeot /* 0x54 - 2048x1152@60Hz */ 48119c468b4SFrançois Tigeot { DRM_MODE("2048x1152", DRM_MODE_TYPE_DRIVER, 162000, 2048, 2074, 48219c468b4SFrançois Tigeot 2154, 2250, 0, 1152, 1153, 1156, 1200, 0, 48319c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 48419c468b4SFrançois Tigeot /* 0x4c - 2560x1600@60Hz RB */ 485d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608, 486d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1646, 0, 487d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 48819c468b4SFrançois Tigeot /* 0x4d - 2560x1600@60Hz */ 489d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, 490d82bf20eSFrançois Tigeot 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 491d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 49219c468b4SFrançois Tigeot /* 0x4e - 2560x1600@75Hz */ 493d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, 494d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, 495d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 49619c468b4SFrançois Tigeot /* 0x4f - 2560x1600@85Hz */ 497d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, 498d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, 499d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 50019c468b4SFrançois Tigeot /* 0x50 - 2560x1600@120Hz RB */ 501d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608, 502d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1694, 0, 503d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 50419c468b4SFrançois Tigeot /* 0x57 - 4096x2160@60Hz RB */ 50519c468b4SFrançois Tigeot { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556744, 4096, 4104, 50619c468b4SFrançois Tigeot 4136, 4176, 0, 2160, 2208, 2216, 2222, 0, 50719c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 50819c468b4SFrançois Tigeot /* 0x58 - 4096x2160@59.94Hz RB */ 50919c468b4SFrançois Tigeot { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104, 51019c468b4SFrançois Tigeot 4136, 4176, 0, 2160, 2208, 2216, 2222, 0, 51119c468b4SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 512d82bf20eSFrançois Tigeot }; 513d82bf20eSFrançois Tigeot 5149edbd4a0SFrançois Tigeot /* 5159edbd4a0SFrançois Tigeot * These more or less come from the DMT spec. The 720x400 modes are 5169edbd4a0SFrançois Tigeot * inferred from historical 80x25 practice. The 640x480@67 and 832x624@75 5179edbd4a0SFrançois Tigeot * modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode 5189edbd4a0SFrançois Tigeot * should be 1152x870, again for the Mac, but instead we use the x864 DMT 5199edbd4a0SFrançois Tigeot * mode. 5209edbd4a0SFrançois Tigeot * 5219edbd4a0SFrançois Tigeot * The DMT modes have been fact-checked; the rest are mild guesses. 5229edbd4a0SFrançois Tigeot */ 523d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_est_modes[] = { 524d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 525d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 526d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ 527d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 528d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 529d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ 530d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 531d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 532d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ 533d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 534c0e85e96SFrançois Tigeot 704, 832, 0, 480, 489, 492, 520, 0, 535d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ 536d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, 537d82bf20eSFrançois Tigeot 768, 864, 0, 480, 483, 486, 525, 0, 538d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ 539c0e85e96SFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 540d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 541d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ 542d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, 543d82bf20eSFrançois Tigeot 846, 900, 0, 400, 421, 423, 449, 0, 544d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ 545d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, 546d82bf20eSFrançois Tigeot 846, 900, 0, 400, 412, 414, 449, 0, 547d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ 548d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 549d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 550d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ 551c0e85e96SFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, 552d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 553d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ 554d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 555d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 556d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ 557d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 558d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 559d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ 560d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, 561d82bf20eSFrançois Tigeot 1208, 1264, 0, 768, 768, 776, 817, 0, 562d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ 563d82bf20eSFrançois Tigeot { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, 564d82bf20eSFrançois Tigeot 928, 1152, 0, 624, 625, 628, 667, 0, 565d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ 566d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 567d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 568d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ 569d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 570d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 571d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ 572d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 573d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 574d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ 575d82bf20eSFrançois Tigeot }; 576d82bf20eSFrançois Tigeot 577d82bf20eSFrançois Tigeot struct minimode { 578d82bf20eSFrançois Tigeot short w; 579d82bf20eSFrançois Tigeot short h; 580d82bf20eSFrançois Tigeot short r; 581d82bf20eSFrançois Tigeot short rb; 582d82bf20eSFrançois Tigeot }; 583d82bf20eSFrançois Tigeot 584d82bf20eSFrançois Tigeot static const struct minimode est3_modes[] = { 585d82bf20eSFrançois Tigeot /* byte 6 */ 586d82bf20eSFrançois Tigeot { 640, 350, 85, 0 }, 587d82bf20eSFrançois Tigeot { 640, 400, 85, 0 }, 588d82bf20eSFrançois Tigeot { 720, 400, 85, 0 }, 589d82bf20eSFrançois Tigeot { 640, 480, 85, 0 }, 590d82bf20eSFrançois Tigeot { 848, 480, 60, 0 }, 591d82bf20eSFrançois Tigeot { 800, 600, 85, 0 }, 592d82bf20eSFrançois Tigeot { 1024, 768, 85, 0 }, 593d82bf20eSFrançois Tigeot { 1152, 864, 75, 0 }, 594d82bf20eSFrançois Tigeot /* byte 7 */ 595d82bf20eSFrançois Tigeot { 1280, 768, 60, 1 }, 596d82bf20eSFrançois Tigeot { 1280, 768, 60, 0 }, 597d82bf20eSFrançois Tigeot { 1280, 768, 75, 0 }, 598d82bf20eSFrançois Tigeot { 1280, 768, 85, 0 }, 599d82bf20eSFrançois Tigeot { 1280, 960, 60, 0 }, 600d82bf20eSFrançois Tigeot { 1280, 960, 85, 0 }, 601d82bf20eSFrançois Tigeot { 1280, 1024, 60, 0 }, 602d82bf20eSFrançois Tigeot { 1280, 1024, 85, 0 }, 603d82bf20eSFrançois Tigeot /* byte 8 */ 604d82bf20eSFrançois Tigeot { 1360, 768, 60, 0 }, 605d82bf20eSFrançois Tigeot { 1440, 900, 60, 1 }, 606d82bf20eSFrançois Tigeot { 1440, 900, 60, 0 }, 607d82bf20eSFrançois Tigeot { 1440, 900, 75, 0 }, 608d82bf20eSFrançois Tigeot { 1440, 900, 85, 0 }, 609d82bf20eSFrançois Tigeot { 1400, 1050, 60, 1 }, 610d82bf20eSFrançois Tigeot { 1400, 1050, 60, 0 }, 611d82bf20eSFrançois Tigeot { 1400, 1050, 75, 0 }, 612d82bf20eSFrançois Tigeot /* byte 9 */ 613d82bf20eSFrançois Tigeot { 1400, 1050, 85, 0 }, 614d82bf20eSFrançois Tigeot { 1680, 1050, 60, 1 }, 615d82bf20eSFrançois Tigeot { 1680, 1050, 60, 0 }, 616d82bf20eSFrançois Tigeot { 1680, 1050, 75, 0 }, 617d82bf20eSFrançois Tigeot { 1680, 1050, 85, 0 }, 618d82bf20eSFrançois Tigeot { 1600, 1200, 60, 0 }, 619d82bf20eSFrançois Tigeot { 1600, 1200, 65, 0 }, 620d82bf20eSFrançois Tigeot { 1600, 1200, 70, 0 }, 621d82bf20eSFrançois Tigeot /* byte 10 */ 622d82bf20eSFrançois Tigeot { 1600, 1200, 75, 0 }, 623d82bf20eSFrançois Tigeot { 1600, 1200, 85, 0 }, 624d82bf20eSFrançois Tigeot { 1792, 1344, 60, 0 }, 6259edbd4a0SFrançois Tigeot { 1792, 1344, 75, 0 }, 626d82bf20eSFrançois Tigeot { 1856, 1392, 60, 0 }, 627d82bf20eSFrançois Tigeot { 1856, 1392, 75, 0 }, 628d82bf20eSFrançois Tigeot { 1920, 1200, 60, 1 }, 629d82bf20eSFrançois Tigeot { 1920, 1200, 60, 0 }, 630d82bf20eSFrançois Tigeot /* byte 11 */ 631d82bf20eSFrançois Tigeot { 1920, 1200, 75, 0 }, 632d82bf20eSFrançois Tigeot { 1920, 1200, 85, 0 }, 633d82bf20eSFrançois Tigeot { 1920, 1440, 60, 0 }, 634d82bf20eSFrançois Tigeot { 1920, 1440, 75, 0 }, 635d82bf20eSFrançois Tigeot }; 636d82bf20eSFrançois Tigeot 637d82bf20eSFrançois Tigeot static const struct minimode extra_modes[] = { 638d82bf20eSFrançois Tigeot { 1024, 576, 60, 0 }, 639d82bf20eSFrançois Tigeot { 1366, 768, 60, 0 }, 640d82bf20eSFrançois Tigeot { 1600, 900, 60, 0 }, 641d82bf20eSFrançois Tigeot { 1680, 945, 60, 0 }, 642d82bf20eSFrançois Tigeot { 1920, 1080, 60, 0 }, 643d82bf20eSFrançois Tigeot { 2048, 1152, 60, 0 }, 644d82bf20eSFrançois Tigeot { 2048, 1536, 60, 0 }, 645d82bf20eSFrançois Tigeot }; 646d82bf20eSFrançois Tigeot 647d82bf20eSFrançois Tigeot /* 648d82bf20eSFrançois Tigeot * Probably taken from CEA-861 spec. 649d82bf20eSFrançois Tigeot * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. 650aee94f86SFrançois Tigeot * 651aee94f86SFrançois Tigeot * Index using the VIC. 652d82bf20eSFrançois Tigeot */ 653d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_cea_modes[] = { 654aee94f86SFrançois Tigeot /* 0 - dummy, VICs start at 1 */ 655aee94f86SFrançois Tigeot { }, 656d82bf20eSFrançois Tigeot /* 1 - 640x480@60Hz */ 657d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 658d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 6599edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6609edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 661d82bf20eSFrançois Tigeot /* 2 - 720x480@60Hz */ 662d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 663d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 6649edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6659edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 666d82bf20eSFrançois Tigeot /* 3 - 720x480@60Hz */ 667d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 668d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 6699edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6709edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 671d82bf20eSFrançois Tigeot /* 4 - 1280x720@60Hz */ 672d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 673d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 6749edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 6759edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 676d82bf20eSFrançois Tigeot /* 5 - 1920x1080i@60Hz */ 677d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 678d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 679d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 6809edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 6819edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 6821b13d190SFrançois Tigeot /* 6 - 720(1440)x480i@60Hz */ 6831b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 6841b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 685d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6869edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 6879edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 6881b13d190SFrançois Tigeot /* 7 - 720(1440)x480i@60Hz */ 6891b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 6901b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 691d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6929edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 6939edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 6941b13d190SFrançois Tigeot /* 8 - 720(1440)x240@60Hz */ 6951b13d190SFrançois Tigeot { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 6961b13d190SFrançois Tigeot 801, 858, 0, 240, 244, 247, 262, 0, 697d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6989edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 6999edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 7001b13d190SFrançois Tigeot /* 9 - 720(1440)x240@60Hz */ 7011b13d190SFrançois Tigeot { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, 7021b13d190SFrançois Tigeot 801, 858, 0, 240, 244, 247, 262, 0, 703d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7049edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 7059edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 706d82bf20eSFrançois Tigeot /* 10 - 2880x480i@60Hz */ 707d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 708d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 709d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7109edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7119edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 712d82bf20eSFrançois Tigeot /* 11 - 2880x480i@60Hz */ 713d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 714d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 715d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7169edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7179edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 718d82bf20eSFrançois Tigeot /* 12 - 2880x240@60Hz */ 719d82bf20eSFrançois Tigeot { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 720d82bf20eSFrançois Tigeot 3204, 3432, 0, 240, 244, 247, 262, 0, 7219edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7229edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 723d82bf20eSFrançois Tigeot /* 13 - 2880x240@60Hz */ 724d82bf20eSFrançois Tigeot { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 725d82bf20eSFrançois Tigeot 3204, 3432, 0, 240, 244, 247, 262, 0, 7269edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7279edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 728d82bf20eSFrançois Tigeot /* 14 - 1440x480@60Hz */ 729d82bf20eSFrançois Tigeot { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, 730d82bf20eSFrançois Tigeot 1596, 1716, 0, 480, 489, 495, 525, 0, 7319edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7329edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 733d82bf20eSFrançois Tigeot /* 15 - 1440x480@60Hz */ 734d82bf20eSFrançois Tigeot { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, 735d82bf20eSFrançois Tigeot 1596, 1716, 0, 480, 489, 495, 525, 0, 7369edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7379edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 738d82bf20eSFrançois Tigeot /* 16 - 1920x1080@60Hz */ 739d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 740d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 7419edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7429edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 743d82bf20eSFrançois Tigeot /* 17 - 720x576@50Hz */ 744d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 745d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 7469edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7479edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 748d82bf20eSFrançois Tigeot /* 18 - 720x576@50Hz */ 749d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 750d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 7519edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7529edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 753d82bf20eSFrançois Tigeot /* 19 - 1280x720@50Hz */ 754d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, 755d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 7569edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7579edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 758d82bf20eSFrançois Tigeot /* 20 - 1920x1080i@50Hz */ 759d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 760d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 761d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 7629edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7639edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 7641b13d190SFrançois Tigeot /* 21 - 720(1440)x576i@50Hz */ 7651b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 7661b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 767d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7689edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 7699edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 7701b13d190SFrançois Tigeot /* 22 - 720(1440)x576i@50Hz */ 7711b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 7721b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 773d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7749edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 7759edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 7761b13d190SFrançois Tigeot /* 23 - 720(1440)x288@50Hz */ 7771b13d190SFrançois Tigeot { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 7781b13d190SFrançois Tigeot 795, 864, 0, 288, 290, 293, 312, 0, 779d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7809edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 7819edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 7821b13d190SFrançois Tigeot /* 24 - 720(1440)x288@50Hz */ 7831b13d190SFrançois Tigeot { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, 7841b13d190SFrançois Tigeot 795, 864, 0, 288, 290, 293, 312, 0, 785d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7869edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 7879edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 788d82bf20eSFrançois Tigeot /* 25 - 2880x576i@50Hz */ 789d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 790d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 791d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7929edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7939edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 794d82bf20eSFrançois Tigeot /* 26 - 2880x576i@50Hz */ 795d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 796d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 797d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7989edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7999edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 800d82bf20eSFrançois Tigeot /* 27 - 2880x288@50Hz */ 801d82bf20eSFrançois Tigeot { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 802d82bf20eSFrançois Tigeot 3180, 3456, 0, 288, 290, 293, 312, 0, 8039edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8049edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 805d82bf20eSFrançois Tigeot /* 28 - 2880x288@50Hz */ 806d82bf20eSFrançois Tigeot { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 807d82bf20eSFrançois Tigeot 3180, 3456, 0, 288, 290, 293, 312, 0, 8089edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8099edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 810d82bf20eSFrançois Tigeot /* 29 - 1440x576@50Hz */ 811d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 812d82bf20eSFrançois Tigeot 1592, 1728, 0, 576, 581, 586, 625, 0, 8139edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8149edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 815d82bf20eSFrançois Tigeot /* 30 - 1440x576@50Hz */ 816d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 817d82bf20eSFrançois Tigeot 1592, 1728, 0, 576, 581, 586, 625, 0, 8189edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8199edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 820d82bf20eSFrançois Tigeot /* 31 - 1920x1080@50Hz */ 821d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 822d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 8239edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8249edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 825d82bf20eSFrançois Tigeot /* 32 - 1920x1080@24Hz */ 826d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, 827d82bf20eSFrançois Tigeot 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, 8289edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8299edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 830d82bf20eSFrançois Tigeot /* 33 - 1920x1080@25Hz */ 831d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 832d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 8339edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8349edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 835d82bf20eSFrançois Tigeot /* 34 - 1920x1080@30Hz */ 836d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 837d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 8389edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8399edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 840d82bf20eSFrançois Tigeot /* 35 - 2880x480@60Hz */ 841d82bf20eSFrançois Tigeot { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, 842d82bf20eSFrançois Tigeot 3192, 3432, 0, 480, 489, 495, 525, 0, 8439edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8449edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 845d82bf20eSFrançois Tigeot /* 36 - 2880x480@60Hz */ 846d82bf20eSFrançois Tigeot { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, 847d82bf20eSFrançois Tigeot 3192, 3432, 0, 480, 489, 495, 525, 0, 8489edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8499edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 850d82bf20eSFrançois Tigeot /* 37 - 2880x576@50Hz */ 851d82bf20eSFrançois Tigeot { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, 852d82bf20eSFrançois Tigeot 3184, 3456, 0, 576, 581, 586, 625, 0, 8539edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8549edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 855d82bf20eSFrançois Tigeot /* 38 - 2880x576@50Hz */ 856d82bf20eSFrançois Tigeot { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, 857d82bf20eSFrançois Tigeot 3184, 3456, 0, 576, 581, 586, 625, 0, 8589edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8599edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 860d82bf20eSFrançois Tigeot /* 39 - 1920x1080i@50Hz */ 861d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, 862d82bf20eSFrançois Tigeot 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, 863d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | 8649edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 8659edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 866d82bf20eSFrançois Tigeot /* 40 - 1920x1080i@100Hz */ 867d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 868d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 869d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 8709edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 8719edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 872d82bf20eSFrançois Tigeot /* 41 - 1280x720@100Hz */ 873d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720, 874d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 8759edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8769edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 877d82bf20eSFrançois Tigeot /* 42 - 720x576@100Hz */ 878d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 879d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8809edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8819edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 882d82bf20eSFrançois Tigeot /* 43 - 720x576@100Hz */ 883d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 884d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8859edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8869edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 8871b13d190SFrançois Tigeot /* 44 - 720(1440)x576i@100Hz */ 8881b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 8891b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 890d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8911b13d190SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 8929edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 8931b13d190SFrançois Tigeot /* 45 - 720(1440)x576i@100Hz */ 8941b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 8951b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 896d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8971b13d190SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 8989edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 899d82bf20eSFrançois Tigeot /* 46 - 1920x1080i@120Hz */ 900d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 901d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 902d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 9039edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 9049edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 905d82bf20eSFrançois Tigeot /* 47 - 1280x720@120Hz */ 906d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390, 907d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 9089edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9099edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 910d82bf20eSFrançois Tigeot /* 48 - 720x480@120Hz */ 911d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 912d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9139edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9149edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 915d82bf20eSFrançois Tigeot /* 49 - 720x480@120Hz */ 916d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 917d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9189edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9199edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 9201b13d190SFrançois Tigeot /* 50 - 720(1440)x480i@120Hz */ 9211b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, 9221b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 923d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9249edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9259edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 9261b13d190SFrançois Tigeot /* 51 - 720(1440)x480i@120Hz */ 9271b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, 9281b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 929d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9309edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9319edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 932d82bf20eSFrançois Tigeot /* 52 - 720x576@200Hz */ 933d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 934d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 9359edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9369edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 937d82bf20eSFrançois Tigeot /* 53 - 720x576@200Hz */ 938d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 939d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 9409edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9419edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 9421b13d190SFrançois Tigeot /* 54 - 720(1440)x576i@200Hz */ 9431b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 9441b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 945d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9469edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9479edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 9481b13d190SFrançois Tigeot /* 55 - 720(1440)x576i@200Hz */ 9491b13d190SFrançois Tigeot { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 9501b13d190SFrançois Tigeot 795, 864, 0, 576, 580, 586, 625, 0, 951d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9529edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9539edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 954d82bf20eSFrançois Tigeot /* 56 - 720x480@240Hz */ 955d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 956d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9579edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9589edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 959d82bf20eSFrançois Tigeot /* 57 - 720x480@240Hz */ 960d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 961d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9629edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9639edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 9641b13d190SFrançois Tigeot /* 58 - 720(1440)x480i@240 */ 9651b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, 9661b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 967d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9689edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9699edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 9701b13d190SFrançois Tigeot /* 59 - 720(1440)x480i@240 */ 9711b13d190SFrançois Tigeot { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, 9721b13d190SFrançois Tigeot 801, 858, 0, 480, 488, 494, 525, 0, 973d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9749edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9759edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 976d82bf20eSFrançois Tigeot /* 60 - 1280x720@24Hz */ 977d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, 978d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 9799edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9809edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 981d82bf20eSFrançois Tigeot /* 61 - 1280x720@25Hz */ 982d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700, 983d82bf20eSFrançois Tigeot 3740, 3960, 0, 720, 725, 730, 750, 0, 9849edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9859edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 986d82bf20eSFrançois Tigeot /* 62 - 1280x720@30Hz */ 987d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040, 988d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 9899edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9909edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 991d82bf20eSFrançois Tigeot /* 63 - 1920x1080@120Hz */ 992d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, 993d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 9949edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9959edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 996d82bf20eSFrançois Tigeot /* 64 - 1920x1080@100Hz */ 997d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, 998d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 9999edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 10009edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 10019edbd4a0SFrançois Tigeot }; 10029edbd4a0SFrançois Tigeot 10039edbd4a0SFrançois Tigeot /* 1004aee94f86SFrançois Tigeot * HDMI 1.4 4k modes. Index using the VIC. 10059edbd4a0SFrançois Tigeot */ 10069edbd4a0SFrançois Tigeot static const struct drm_display_mode edid_4k_modes[] = { 1007aee94f86SFrançois Tigeot /* 0 - dummy, VICs start at 1 */ 1008aee94f86SFrançois Tigeot { }, 10099edbd4a0SFrançois Tigeot /* 1 - 3840x2160@30Hz */ 10109edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 10119edbd4a0SFrançois Tigeot 3840, 4016, 4104, 4400, 0, 10129edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 10139edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 10149edbd4a0SFrançois Tigeot .vrefresh = 30, }, 10159edbd4a0SFrançois Tigeot /* 2 - 3840x2160@25Hz */ 10169edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 10179edbd4a0SFrançois Tigeot 3840, 4896, 4984, 5280, 0, 10189edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 10199edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 10209edbd4a0SFrançois Tigeot .vrefresh = 25, }, 10219edbd4a0SFrançois Tigeot /* 3 - 3840x2160@24Hz */ 10229edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 10239edbd4a0SFrançois Tigeot 3840, 5116, 5204, 5500, 0, 10249edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 10259edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 10269edbd4a0SFrançois Tigeot .vrefresh = 24, }, 10279edbd4a0SFrançois Tigeot /* 4 - 4096x2160@24Hz (SMPTE) */ 10289edbd4a0SFrançois Tigeot { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 10299edbd4a0SFrançois Tigeot 4096, 5116, 5204, 5500, 0, 10309edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 10319edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 10329edbd4a0SFrançois Tigeot .vrefresh = 24, }, 1033d82bf20eSFrançois Tigeot }; 1034d82bf20eSFrançois Tigeot 10355718399fSFrançois Tigeot /*** DDC fetch and block validation ***/ 10365718399fSFrançois Tigeot 10375718399fSFrançois Tigeot static const u8 edid_header[] = { 10385718399fSFrançois Tigeot 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 10395718399fSFrançois Tigeot }; 10405718399fSFrançois Tigeot 1041ba55f2f5SFrançois Tigeot /** 1042ba55f2f5SFrançois Tigeot * drm_edid_header_is_valid - sanity check the header of the base EDID block 1043ba55f2f5SFrançois Tigeot * @raw_edid: pointer to raw base EDID block 1044ba55f2f5SFrançois Tigeot * 1045ba55f2f5SFrançois Tigeot * Sanity check the header of the base EDID block. 1046ba55f2f5SFrançois Tigeot * 1047ba55f2f5SFrançois Tigeot * Return: 8 if the header is perfect, down to 0 if it's totally wrong. 10485718399fSFrançois Tigeot */ 10495718399fSFrançois Tigeot int drm_edid_header_is_valid(const u8 *raw_edid) 10505718399fSFrançois Tigeot { 10515718399fSFrançois Tigeot int i, score = 0; 10525718399fSFrançois Tigeot 10535718399fSFrançois Tigeot for (i = 0; i < sizeof(edid_header); i++) 10545718399fSFrançois Tigeot if (raw_edid[i] == edid_header[i]) 10555718399fSFrançois Tigeot score++; 10565718399fSFrançois Tigeot 10575718399fSFrançois Tigeot return score; 10585718399fSFrançois Tigeot } 1059ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_header_is_valid); 1060ce3d36d7SFrançois Tigeot 1061ce3d36d7SFrançois Tigeot static int edid_fixup __read_mostly = 6; 1062d82bf20eSFrançois Tigeot module_param_named(edid_fixup, edid_fixup, int, 0400); 1063d82bf20eSFrançois Tigeot MODULE_PARM_DESC(edid_fixup, 1064d82bf20eSFrançois Tigeot "Minimum number of valid EDID header bytes (0-8, default 6)"); 10655718399fSFrançois Tigeot 10668621f407SFrançois Tigeot static void drm_get_displayid(struct drm_connector *connector, 10678621f407SFrançois Tigeot struct edid *edid); 10688621f407SFrançois Tigeot 10692c9916cdSFrançois Tigeot static int drm_edid_block_checksum(const u8 *raw_edid) 10702c9916cdSFrançois Tigeot { 10712c9916cdSFrançois Tigeot int i; 10722c9916cdSFrançois Tigeot u8 csum = 0; 10732c9916cdSFrançois Tigeot for (i = 0; i < EDID_LENGTH; i++) 10742c9916cdSFrançois Tigeot csum += raw_edid[i]; 10752c9916cdSFrançois Tigeot 10762c9916cdSFrançois Tigeot return csum; 10772c9916cdSFrançois Tigeot } 10782c9916cdSFrançois Tigeot 10792c9916cdSFrançois Tigeot static bool drm_edid_is_zero(const u8 *in_edid, int length) 10802c9916cdSFrançois Tigeot { 10812c9916cdSFrançois Tigeot if (memchr_inv(in_edid, 0, length)) 10822c9916cdSFrançois Tigeot return false; 10832c9916cdSFrançois Tigeot 10842c9916cdSFrançois Tigeot return true; 10852c9916cdSFrançois Tigeot } 10862c9916cdSFrançois Tigeot 1087ba55f2f5SFrançois Tigeot /** 1088ba55f2f5SFrançois Tigeot * drm_edid_block_valid - Sanity check the EDID block (base or extension) 1089ba55f2f5SFrançois Tigeot * @raw_edid: pointer to raw EDID block 1090ba55f2f5SFrançois Tigeot * @block: type of block to validate (0 for base, extension otherwise) 1091ba55f2f5SFrançois Tigeot * @print_bad_edid: if true, dump bad EDID blocks to the console 109219c468b4SFrançois Tigeot * @edid_corrupt: if true, the header or checksum is invalid 1093ba55f2f5SFrançois Tigeot * 1094ba55f2f5SFrançois Tigeot * Validate a base or extension EDID block and optionally dump bad blocks to 1095ba55f2f5SFrançois Tigeot * the console. 1096ba55f2f5SFrançois Tigeot * 1097ba55f2f5SFrançois Tigeot * Return: True if the block is valid, false otherwise. 10985718399fSFrançois Tigeot */ 109919c468b4SFrançois Tigeot bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, 110019c468b4SFrançois Tigeot bool *edid_corrupt) 11015718399fSFrançois Tigeot { 11022c9916cdSFrançois Tigeot u8 csum; 11035718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 11045718399fSFrançois Tigeot 11059edbd4a0SFrançois Tigeot if (WARN_ON(!raw_edid)) 11069edbd4a0SFrançois Tigeot return false; 11079edbd4a0SFrançois Tigeot 1108ce3d36d7SFrançois Tigeot if (edid_fixup > 8 || edid_fixup < 0) 1109ce3d36d7SFrançois Tigeot edid_fixup = 6; 1110ce3d36d7SFrançois Tigeot 1111ce3d36d7SFrançois Tigeot if (block == 0) { 11125718399fSFrançois Tigeot int score = drm_edid_header_is_valid(raw_edid); 111319c468b4SFrançois Tigeot if (score == 8) { 111419c468b4SFrançois Tigeot if (edid_corrupt) 111519c468b4SFrançois Tigeot *edid_corrupt = false; 111619c468b4SFrançois Tigeot } else if (score >= edid_fixup) { 111719c468b4SFrançois Tigeot /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 111819c468b4SFrançois Tigeot * The corrupt flag needs to be set here otherwise, the 111919c468b4SFrançois Tigeot * fix-up code here will correct the problem, the 112019c468b4SFrançois Tigeot * checksum is correct and the test fails 112119c468b4SFrançois Tigeot */ 112219c468b4SFrançois Tigeot if (edid_corrupt) 112319c468b4SFrançois Tigeot *edid_corrupt = true; 11245718399fSFrançois Tigeot DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); 11255718399fSFrançois Tigeot memcpy(raw_edid, edid_header, sizeof(edid_header)); 11265718399fSFrançois Tigeot } else { 112719c468b4SFrançois Tigeot if (edid_corrupt) 112819c468b4SFrançois Tigeot *edid_corrupt = true; 11295718399fSFrançois Tigeot goto bad; 11305718399fSFrançois Tigeot } 11315718399fSFrançois Tigeot } 11325718399fSFrançois Tigeot 11332c9916cdSFrançois Tigeot csum = drm_edid_block_checksum(raw_edid); 11345718399fSFrançois Tigeot if (csum) { 1135ce3d36d7SFrançois Tigeot if (print_bad_edid) { 11365718399fSFrançois Tigeot DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); 1137ce3d36d7SFrançois Tigeot } 11385718399fSFrançois Tigeot 113919c468b4SFrançois Tigeot if (edid_corrupt) 114019c468b4SFrançois Tigeot *edid_corrupt = true; 114119c468b4SFrançois Tigeot 11425718399fSFrançois Tigeot /* allow CEA to slide through, switches mangle this */ 11435718399fSFrançois Tigeot if (raw_edid[0] != 0x02) 11445718399fSFrançois Tigeot goto bad; 11455718399fSFrançois Tigeot } 11465718399fSFrançois Tigeot 11475718399fSFrançois Tigeot /* per-block-type checks */ 11485718399fSFrançois Tigeot switch (raw_edid[0]) { 11495718399fSFrançois Tigeot case 0: /* base */ 11505718399fSFrançois Tigeot if (edid->version != 1) { 11515718399fSFrançois Tigeot DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); 11525718399fSFrançois Tigeot goto bad; 11535718399fSFrançois Tigeot } 11545718399fSFrançois Tigeot 11555718399fSFrançois Tigeot if (edid->revision > 4) 11565718399fSFrançois Tigeot DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); 11575718399fSFrançois Tigeot break; 11585718399fSFrançois Tigeot 11595718399fSFrançois Tigeot default: 11605718399fSFrançois Tigeot break; 11615718399fSFrançois Tigeot } 11625718399fSFrançois Tigeot 11639edbd4a0SFrançois Tigeot return true; 11645718399fSFrançois Tigeot 11655718399fSFrançois Tigeot bad: 11669edbd4a0SFrançois Tigeot if (print_bad_edid) { 11672c9916cdSFrançois Tigeot if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) { 11682c9916cdSFrançois Tigeot printk(KERN_ERR "EDID block is all zeroes\n"); 11692c9916cdSFrançois Tigeot } else { 11709edbd4a0SFrançois Tigeot printk(KERN_ERR "Raw EDID:\n"); 11718621f407SFrançois Tigeot for (int i = 0; i < EDID_LENGTH; ) { 11725718399fSFrançois Tigeot kprintf("%02x", raw_edid[i]); 11735718399fSFrançois Tigeot i++; 11745718399fSFrançois Tigeot if (i % 16 == 0 || i == EDID_LENGTH) 11755718399fSFrançois Tigeot kprintf("\n"); 11765718399fSFrançois Tigeot else if (i % 8 == 0) 11775718399fSFrançois Tigeot kprintf(" "); 11785718399fSFrançois Tigeot else 11795718399fSFrançois Tigeot kprintf(" "); 11805718399fSFrançois Tigeot } 11815718399fSFrançois Tigeot } 11822c9916cdSFrançois Tigeot } 11839edbd4a0SFrançois Tigeot return false; 11845718399fSFrançois Tigeot } 1185ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_block_valid); 11865718399fSFrançois Tigeot 11875718399fSFrançois Tigeot /** 11885718399fSFrançois Tigeot * drm_edid_is_valid - sanity check EDID data 11895718399fSFrançois Tigeot * @edid: EDID data 11905718399fSFrançois Tigeot * 11915718399fSFrançois Tigeot * Sanity-check an entire EDID record (including extensions) 1192ba55f2f5SFrançois Tigeot * 1193ba55f2f5SFrançois Tigeot * Return: True if the EDID data is valid, false otherwise. 11945718399fSFrançois Tigeot */ 11955718399fSFrançois Tigeot bool drm_edid_is_valid(struct edid *edid) 11965718399fSFrançois Tigeot { 11975718399fSFrançois Tigeot int i; 11985718399fSFrançois Tigeot u8 *raw = (u8 *)edid; 11995718399fSFrançois Tigeot 12005718399fSFrançois Tigeot if (!edid) 12015718399fSFrançois Tigeot return false; 12025718399fSFrançois Tigeot 12035718399fSFrançois Tigeot for (i = 0; i <= edid->extensions; i++) 120419c468b4SFrançois Tigeot if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) 12055718399fSFrançois Tigeot return false; 12065718399fSFrançois Tigeot 12075718399fSFrançois Tigeot return true; 12085718399fSFrançois Tigeot } 1209ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_is_valid); 12105718399fSFrançois Tigeot 12115718399fSFrançois Tigeot #define DDC_SEGMENT_ADDR 0x30 12125718399fSFrançois Tigeot /** 1213ba55f2f5SFrançois Tigeot * drm_do_probe_ddc_edid() - get EDID information via I2C 12142c9916cdSFrançois Tigeot * @data: I2C device adapter 1215ba55f2f5SFrançois Tigeot * @buf: EDID data buffer to be filled 1216ba55f2f5SFrançois Tigeot * @block: 128 byte EDID block to start fetching from 1217ba55f2f5SFrançois Tigeot * @len: EDID data buffer length to fetch 12185718399fSFrançois Tigeot * 1219ba55f2f5SFrançois Tigeot * Try to fetch EDID information by calling I2C driver functions. 12205718399fSFrançois Tigeot * 1221ba55f2f5SFrançois Tigeot * Return: 0 on success or -1 on failure. 12225718399fSFrançois Tigeot */ 12235718399fSFrançois Tigeot static int 12242c9916cdSFrançois Tigeot drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) 12255718399fSFrançois Tigeot { 12262c9916cdSFrançois Tigeot struct i2c_adapter *adapter = data; 12275718399fSFrançois Tigeot unsigned char start = block * EDID_LENGTH; 12286f486c69SFrançois Tigeot unsigned char segment = block >> 1; 12296f486c69SFrançois Tigeot unsigned char xfers = segment ? 3 : 2; 12305718399fSFrançois Tigeot int ret, retries = 5; 12315718399fSFrançois Tigeot 1232ba55f2f5SFrançois Tigeot /* 1233ba55f2f5SFrançois Tigeot * The core I2C driver will automatically retry the transfer if the 12345718399fSFrançois Tigeot * adapter reports EAGAIN. However, we find that bit-banging transfers 12355718399fSFrançois Tigeot * are susceptible to errors under a heavily loaded machine and 12365718399fSFrançois Tigeot * generate spurious NAKs and timeouts. Retrying the transfer 12375718399fSFrançois Tigeot * of the individual block a few times seems to overcome this. 12385718399fSFrançois Tigeot */ 12395718399fSFrançois Tigeot do { 12406e29dde8SFrançois Tigeot struct i2c_msg msgs[] = { 12415718399fSFrançois Tigeot { 12429f4ca867SFrançois Tigeot .addr = DDC_SEGMENT_ADDR, 12439f4ca867SFrançois Tigeot .flags = 0, 12449f4ca867SFrançois Tigeot .len = 1, 12459f4ca867SFrançois Tigeot .buf = &segment, 12469f4ca867SFrançois Tigeot }, { 12479f4ca867SFrançois Tigeot .addr = DDC_ADDR, 12489f4ca867SFrançois Tigeot .flags = 0, 12499f4ca867SFrançois Tigeot .len = 1, 12509f4ca867SFrançois Tigeot .buf = &start, 12519f4ca867SFrançois Tigeot }, { 12529f4ca867SFrançois Tigeot .addr = DDC_ADDR, 12539f4ca867SFrançois Tigeot .flags = I2C_M_RD, 12549f4ca867SFrançois Tigeot .len = len, 12559f4ca867SFrançois Tigeot .buf = buf, 12569f4ca867SFrançois Tigeot } 12579f4ca867SFrançois Tigeot }; 12589f4ca867SFrançois Tigeot 12599f4ca867SFrançois Tigeot /* 12609f4ca867SFrançois Tigeot * Avoid sending the segment addr to not upset non-compliant 12619f4ca867SFrançois Tigeot * DDC monitors. 12629f4ca867SFrançois Tigeot */ 12639f4ca867SFrançois Tigeot ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers); 12649f4ca867SFrançois Tigeot 12659f4ca867SFrançois Tigeot if (ret == -ENXIO) { 12669f4ca867SFrançois Tigeot DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n", 12679f4ca867SFrançois Tigeot adapter->name); 12689f4ca867SFrançois Tigeot break; 12699f4ca867SFrançois Tigeot } 12709f4ca867SFrançois Tigeot } while (ret != xfers && --retries); 12719f4ca867SFrançois Tigeot 12729f4ca867SFrançois Tigeot return ret == xfers ? 0 : -1; 12739f4ca867SFrançois Tigeot } 12749f4ca867SFrançois Tigeot 12759f4ca867SFrançois Tigeot /* 12769f4ca867SFrançois Tigeot * Old version of drm_do_probe_ddc_edid, still using 12779f4ca867SFrançois Tigeot * the FreeBSD/DragonFly iic API 12789f4ca867SFrançois Tigeot */ 12799f4ca867SFrançois Tigeot static int 12809f4ca867SFrançois Tigeot drm_do_probe_ddc_edid_iic(void *data, u8 *buf, unsigned int block, size_t len) 12819f4ca867SFrançois Tigeot { 12829f4ca867SFrançois Tigeot device_t adapter = data; 12839f4ca867SFrançois Tigeot unsigned char start = block * EDID_LENGTH; 12849f4ca867SFrançois Tigeot unsigned char segment = block >> 1; 12859f4ca867SFrançois Tigeot unsigned char xfers = segment ? 3 : 2; 12869f4ca867SFrançois Tigeot int ret, retries = 5; 12879f4ca867SFrançois Tigeot 12889f4ca867SFrançois Tigeot /* 12899f4ca867SFrançois Tigeot * The core I2C driver will automatically retry the transfer if the 12909f4ca867SFrançois Tigeot * adapter reports EAGAIN. However, we find that bit-banging transfers 12919f4ca867SFrançois Tigeot * are susceptible to errors under a heavily loaded machine and 12929f4ca867SFrançois Tigeot * generate spurious NAKs and timeouts. Retrying the transfer 12939f4ca867SFrançois Tigeot * of the individual block a few times seems to overcome this. 12949f4ca867SFrançois Tigeot */ 12959f4ca867SFrançois Tigeot do { 12969f4ca867SFrançois Tigeot struct iic_msg msgs[] = { 12979f4ca867SFrançois Tigeot { 12986f486c69SFrançois Tigeot .slave = DDC_SEGMENT_ADDR << 1, 12996f486c69SFrançois Tigeot .flags = 0, 13006f486c69SFrançois Tigeot .len = 1, 13016f486c69SFrançois Tigeot .buf = &segment, 13026f486c69SFrançois Tigeot }, { 13035718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 13046e29dde8SFrançois Tigeot .flags = 0, 13055718399fSFrançois Tigeot .len = 1, 13065718399fSFrançois Tigeot .buf = &start, 13075718399fSFrançois Tigeot }, { 13085718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 13096e29dde8SFrançois Tigeot .flags = I2C_M_RD, 13105718399fSFrançois Tigeot .len = len, 13115718399fSFrançois Tigeot .buf = buf, 13125718399fSFrançois Tigeot } 13135718399fSFrançois Tigeot }; 13146f486c69SFrançois Tigeot 13156f486c69SFrançois Tigeot /* 1316ba55f2f5SFrançois Tigeot * Avoid sending the segment addr to not upset non-compliant 1317ba55f2f5SFrançois Tigeot * DDC monitors. 13186f486c69SFrançois Tigeot */ 13196f486c69SFrançois Tigeot ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers); 13205718399fSFrançois Tigeot if (ret != 0) 13215718399fSFrançois Tigeot DRM_DEBUG_KMS("iicbus_transfer countdown %d error %d\n", 13225718399fSFrançois Tigeot retries, ret); 13235718399fSFrançois Tigeot } while (ret != 0 && --retries); 13245718399fSFrançois Tigeot 13255718399fSFrançois Tigeot return (ret == 0 ? 0 : -1); 13265718399fSFrançois Tigeot } 13275718399fSFrançois Tigeot 13288621f407SFrançois Tigeot /* 13298621f407SFrançois Tigeot * Old version of drm_probe_ddc(), still using 13308621f407SFrançois Tigeot * the FreeBSD/DragonFly iic API 13318621f407SFrançois Tigeot */ 13328621f407SFrançois Tigeot static bool 13338621f407SFrançois Tigeot drm_probe_ddc_iic(device_t adapter) 13348621f407SFrançois Tigeot { 13358621f407SFrançois Tigeot unsigned char out; 13368621f407SFrançois Tigeot 13378621f407SFrançois Tigeot return (drm_do_probe_ddc_edid_iic(adapter, &out, 0, 1) == 0); 13388621f407SFrançois Tigeot } 13398621f407SFrançois Tigeot 13408621f407SFrançois Tigeot /* 13418621f407SFrançois Tigeot * Old version of drm_get_edid(), still using 13428621f407SFrançois Tigeot * the FreeBSD/DragonFly iic API 13438621f407SFrançois Tigeot */ 13448621f407SFrançois Tigeot struct edid *drm_get_edid_iic(struct drm_connector *connector, 13458621f407SFrançois Tigeot device_t adapter) 13468621f407SFrançois Tigeot { 13478621f407SFrançois Tigeot if (!drm_probe_ddc_iic(adapter)) 13488621f407SFrançois Tigeot return NULL; 13498621f407SFrançois Tigeot 13508621f407SFrançois Tigeot return drm_do_get_edid(connector, drm_do_probe_ddc_edid_iic, adapter); 13518621f407SFrançois Tigeot } 13528621f407SFrançois Tigeot 13532c9916cdSFrançois Tigeot /** 13542c9916cdSFrançois Tigeot * drm_do_get_edid - get EDID data using a custom EDID block read function 13552c9916cdSFrançois Tigeot * @connector: connector we're probing 13562c9916cdSFrançois Tigeot * @get_edid_block: EDID block read function 13572c9916cdSFrançois Tigeot * @data: private data passed to the block read function 13582c9916cdSFrançois Tigeot * 13592c9916cdSFrançois Tigeot * When the I2C adapter connected to the DDC bus is hidden behind a device that 13602c9916cdSFrançois Tigeot * exposes a different interface to read EDID blocks this function can be used 13612c9916cdSFrançois Tigeot * to get EDID data using a custom block read function. 13622c9916cdSFrançois Tigeot * 13632c9916cdSFrançois Tigeot * As in the general case the DDC bus is accessible by the kernel at the I2C 13642c9916cdSFrançois Tigeot * level, drivers must make all reasonable efforts to expose it as an I2C 13652c9916cdSFrançois Tigeot * adapter and use drm_get_edid() instead of abusing this function. 13662c9916cdSFrançois Tigeot * 13672c9916cdSFrançois Tigeot * Return: Pointer to valid EDID or NULL if we couldn't find any. 13682c9916cdSFrançois Tigeot */ 13692c9916cdSFrançois Tigeot struct edid *drm_do_get_edid(struct drm_connector *connector, 13702c9916cdSFrançois Tigeot int (*get_edid_block)(void *data, u8 *buf, unsigned int block, 13712c9916cdSFrançois Tigeot size_t len), 13722c9916cdSFrançois Tigeot void *data) 13735718399fSFrançois Tigeot { 13745718399fSFrançois Tigeot int i, j = 0, valid_extensions = 0; 13755718399fSFrançois Tigeot u8 *block, *new; 1376ce3d36d7SFrançois Tigeot bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); 13775718399fSFrançois Tigeot 13789edbd4a0SFrançois Tigeot if ((block = kmalloc(EDID_LENGTH, M_DRM, M_WAITOK)) == NULL) 13799edbd4a0SFrançois Tigeot return NULL; 13805718399fSFrançois Tigeot 13815718399fSFrançois Tigeot /* base block fetch */ 13825718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 13832c9916cdSFrançois Tigeot if (get_edid_block(data, block, 0, EDID_LENGTH)) 13845718399fSFrançois Tigeot goto out; 138519c468b4SFrançois Tigeot if (drm_edid_block_valid(block, 0, print_bad_edid, 138619c468b4SFrançois Tigeot &connector->edid_corrupt)) 13875718399fSFrançois Tigeot break; 13885718399fSFrançois Tigeot if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { 13895718399fSFrançois Tigeot connector->null_edid_counter++; 13905718399fSFrançois Tigeot goto carp; 13915718399fSFrançois Tigeot } 13925718399fSFrançois Tigeot } 13935718399fSFrançois Tigeot if (i == 4) 13945718399fSFrançois Tigeot goto carp; 13955718399fSFrançois Tigeot 13965718399fSFrançois Tigeot /* if there's no extensions, we're done */ 13975718399fSFrançois Tigeot if (block[0x7e] == 0) 13982c9916cdSFrançois Tigeot return (struct edid *)block; 13995718399fSFrançois Tigeot 1400d82bf20eSFrançois Tigeot new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 1401ce3d36d7SFrançois Tigeot if (!new) 1402ce3d36d7SFrançois Tigeot goto out; 14035718399fSFrançois Tigeot block = new; 14045718399fSFrançois Tigeot 14055718399fSFrançois Tigeot for (j = 1; j <= block[0x7e]; j++) { 14065718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 14072c9916cdSFrançois Tigeot if (get_edid_block(data, 14085718399fSFrançois Tigeot block + (valid_extensions + 1) * EDID_LENGTH, 14095718399fSFrançois Tigeot j, EDID_LENGTH)) 14105718399fSFrançois Tigeot goto out; 141119c468b4SFrançois Tigeot if (drm_edid_block_valid(block + (valid_extensions + 1) 141219c468b4SFrançois Tigeot * EDID_LENGTH, j, 141319c468b4SFrançois Tigeot print_bad_edid, 141419c468b4SFrançois Tigeot NULL)) { 14155718399fSFrançois Tigeot valid_extensions++; 14165718399fSFrançois Tigeot break; 14175718399fSFrançois Tigeot } 14185718399fSFrançois Tigeot } 1419d82bf20eSFrançois Tigeot 1420d82bf20eSFrançois Tigeot if (i == 4 && print_bad_edid) { 1421ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, 1422ce3d36d7SFrançois Tigeot "%s: Ignoring invalid EDID block %d.\n", 1423ba55f2f5SFrançois Tigeot connector->name, j); 1424d82bf20eSFrançois Tigeot 1425d82bf20eSFrançois Tigeot connector->bad_edid_counter++; 1426d82bf20eSFrançois Tigeot } 14275718399fSFrançois Tigeot } 14285718399fSFrançois Tigeot 14295718399fSFrançois Tigeot if (valid_extensions != block[0x7e]) { 14305718399fSFrançois Tigeot block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; 14315718399fSFrançois Tigeot block[0x7e] = valid_extensions; 1432ba55f2f5SFrançois Tigeot new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 1433ce3d36d7SFrançois Tigeot if (!new) 1434ce3d36d7SFrançois Tigeot goto out; 14355718399fSFrançois Tigeot block = new; 14365718399fSFrançois Tigeot } 14375718399fSFrançois Tigeot 14382c9916cdSFrançois Tigeot return (struct edid *)block; 14395718399fSFrançois Tigeot 14405718399fSFrançois Tigeot carp: 1441ce3d36d7SFrançois Tigeot if (print_bad_edid) { 1442ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", 1443ba55f2f5SFrançois Tigeot connector->name, j); 1444ce3d36d7SFrançois Tigeot } 1445ce3d36d7SFrançois Tigeot connector->bad_edid_counter++; 14465718399fSFrançois Tigeot 14475718399fSFrançois Tigeot out: 1448158486a6SFrançois Tigeot kfree(block); 14495718399fSFrançois Tigeot return NULL; 14505718399fSFrançois Tigeot } 14515718399fSFrançois Tigeot 14525718399fSFrançois Tigeot /** 1453ba55f2f5SFrançois Tigeot * drm_probe_ddc() - probe DDC presence 1454ba55f2f5SFrançois Tigeot * @adapter: I2C adapter to probe 14555718399fSFrançois Tigeot * 1456ba55f2f5SFrançois Tigeot * Return: True on success, false on failure. 14575718399fSFrançois Tigeot */ 1458ce3d36d7SFrançois Tigeot bool 14595d302545SFrançois Tigeot drm_probe_ddc(struct i2c_adapter *adapter) 14605718399fSFrançois Tigeot { 14615718399fSFrançois Tigeot unsigned char out; 14625718399fSFrançois Tigeot 14635718399fSFrançois Tigeot return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0); 14645718399fSFrançois Tigeot } 1465ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_probe_ddc); 14665718399fSFrançois Tigeot 14675718399fSFrançois Tigeot /** 14685718399fSFrançois Tigeot * drm_get_edid - get EDID data, if available 14695718399fSFrançois Tigeot * @connector: connector we're probing 1470ba55f2f5SFrançois Tigeot * @adapter: I2C adapter to use for DDC 14715718399fSFrançois Tigeot * 1472ba55f2f5SFrançois Tigeot * Poke the given I2C channel to grab EDID data if possible. If found, 14735718399fSFrançois Tigeot * attach it to the connector. 14745718399fSFrançois Tigeot * 1475ba55f2f5SFrançois Tigeot * Return: Pointer to valid EDID or NULL if we couldn't find any. 14765718399fSFrançois Tigeot */ 14775718399fSFrançois Tigeot struct edid *drm_get_edid(struct drm_connector *connector, 14785d302545SFrançois Tigeot struct i2c_adapter *adapter) 14795718399fSFrançois Tigeot { 14808621f407SFrançois Tigeot struct edid *edid; 14818621f407SFrançois Tigeot 14822c9916cdSFrançois Tigeot if (!drm_probe_ddc(adapter)) 14832c9916cdSFrançois Tigeot return NULL; 14845718399fSFrançois Tigeot 14858621f407SFrançois Tigeot edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); 14868621f407SFrançois Tigeot if (edid) 14878621f407SFrançois Tigeot drm_get_displayid(connector, edid); 14888621f407SFrançois Tigeot return edid; 14895718399fSFrançois Tigeot } 1490af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid); 14915718399fSFrançois Tigeot 14929edbd4a0SFrançois Tigeot /** 1493c0e85e96SFrançois Tigeot * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output 1494c0e85e96SFrançois Tigeot * @connector: connector we're probing 1495c0e85e96SFrançois Tigeot * @adapter: I2C adapter to use for DDC 1496c0e85e96SFrançois Tigeot * 1497c0e85e96SFrançois Tigeot * Wrapper around drm_get_edid() for laptops with dual GPUs using one set of 1498c0e85e96SFrançois Tigeot * outputs. The wrapper adds the requisite vga_switcheroo calls to temporarily 1499c0e85e96SFrançois Tigeot * switch DDC to the GPU which is retrieving EDID. 1500c0e85e96SFrançois Tigeot * 1501c0e85e96SFrançois Tigeot * Return: Pointer to valid EDID or %NULL if we couldn't find any. 1502c0e85e96SFrançois Tigeot */ 1503c0e85e96SFrançois Tigeot struct edid *drm_get_edid_switcheroo(struct drm_connector *connector, 1504c0e85e96SFrançois Tigeot struct i2c_adapter *adapter) 1505c0e85e96SFrançois Tigeot { 1506c0e85e96SFrançois Tigeot struct pci_dev *pdev = connector->dev->pdev; 1507c0e85e96SFrançois Tigeot struct edid *edid; 1508c0e85e96SFrançois Tigeot 1509c0e85e96SFrançois Tigeot vga_switcheroo_lock_ddc(pdev); 1510c0e85e96SFrançois Tigeot edid = drm_get_edid(connector, adapter); 1511c0e85e96SFrançois Tigeot vga_switcheroo_unlock_ddc(pdev); 1512c0e85e96SFrançois Tigeot 1513c0e85e96SFrançois Tigeot return edid; 1514c0e85e96SFrançois Tigeot } 1515c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid_switcheroo); 1516c0e85e96SFrançois Tigeot 1517c0e85e96SFrançois Tigeot /** 15189edbd4a0SFrançois Tigeot * drm_edid_duplicate - duplicate an EDID and the extensions 15199edbd4a0SFrançois Tigeot * @edid: EDID to duplicate 15209edbd4a0SFrançois Tigeot * 1521ba55f2f5SFrançois Tigeot * Return: Pointer to duplicated EDID or NULL on allocation failure. 15229edbd4a0SFrançois Tigeot */ 15239edbd4a0SFrançois Tigeot struct edid *drm_edid_duplicate(const struct edid *edid) 15249edbd4a0SFrançois Tigeot { 15259edbd4a0SFrançois Tigeot return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); 15269edbd4a0SFrançois Tigeot } 15279edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_duplicate); 15289edbd4a0SFrançois Tigeot 15295718399fSFrançois Tigeot /*** EDID parsing ***/ 15305718399fSFrançois Tigeot 15315718399fSFrançois Tigeot /** 15325718399fSFrançois Tigeot * edid_vendor - match a string against EDID's obfuscated vendor field 15335718399fSFrançois Tigeot * @edid: EDID to match 15345718399fSFrançois Tigeot * @vendor: vendor string 15355718399fSFrançois Tigeot * 15365718399fSFrançois Tigeot * Returns true if @vendor is in @edid, false otherwise 15375718399fSFrançois Tigeot */ 15385718399fSFrançois Tigeot static bool edid_vendor(struct edid *edid, char *vendor) 15395718399fSFrançois Tigeot { 15405718399fSFrançois Tigeot char edid_vendor[3]; 15415718399fSFrançois Tigeot 15425718399fSFrançois Tigeot edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; 15435718399fSFrançois Tigeot edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | 15445718399fSFrançois Tigeot ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; 15455718399fSFrançois Tigeot edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; 15465718399fSFrançois Tigeot 15475718399fSFrançois Tigeot return !strncmp(edid_vendor, vendor, 3); 15485718399fSFrançois Tigeot } 15495718399fSFrançois Tigeot 15505718399fSFrançois Tigeot /** 15515718399fSFrançois Tigeot * edid_get_quirks - return quirk flags for a given EDID 15525718399fSFrançois Tigeot * @edid: EDID to process 15535718399fSFrançois Tigeot * 15545718399fSFrançois Tigeot * This tells subsequent routines what fixes they need to apply. 15555718399fSFrançois Tigeot */ 15565718399fSFrançois Tigeot static u32 edid_get_quirks(struct edid *edid) 15575718399fSFrançois Tigeot { 15585718399fSFrançois Tigeot struct edid_quirk *quirk; 15595718399fSFrançois Tigeot int i; 15605718399fSFrançois Tigeot 15616e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { 15625718399fSFrançois Tigeot quirk = &edid_quirk_list[i]; 15635718399fSFrançois Tigeot 15645718399fSFrançois Tigeot if (edid_vendor(edid, quirk->vendor) && 15655718399fSFrançois Tigeot (EDID_PRODUCT_ID(edid) == quirk->product_id)) 15665718399fSFrançois Tigeot return quirk->quirks; 15675718399fSFrançois Tigeot } 15685718399fSFrançois Tigeot 15695718399fSFrançois Tigeot return 0; 15705718399fSFrançois Tigeot } 15715718399fSFrançois Tigeot 15725718399fSFrançois Tigeot #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) 15739edbd4a0SFrançois Tigeot #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t))) 15745718399fSFrançois Tigeot 15755718399fSFrançois Tigeot /** 15765718399fSFrançois Tigeot * edid_fixup_preferred - set preferred modes based on quirk list 15775718399fSFrançois Tigeot * @connector: has mode list to fix up 15785718399fSFrançois Tigeot * @quirks: quirks list 15795718399fSFrançois Tigeot * 15805718399fSFrançois Tigeot * Walk the mode list for @connector, clearing the preferred status 15815718399fSFrançois Tigeot * on existing modes and setting it anew for the right mode ala @quirks. 15825718399fSFrançois Tigeot */ 15835718399fSFrançois Tigeot static void edid_fixup_preferred(struct drm_connector *connector, 15845718399fSFrançois Tigeot u32 quirks) 15855718399fSFrançois Tigeot { 15865718399fSFrançois Tigeot struct drm_display_mode *t, *cur_mode, *preferred_mode; 15875718399fSFrançois Tigeot int target_refresh = 0; 15889edbd4a0SFrançois Tigeot int cur_vrefresh, preferred_vrefresh; 15895718399fSFrançois Tigeot 15905718399fSFrançois Tigeot if (list_empty(&connector->probed_modes)) 15915718399fSFrançois Tigeot return; 15925718399fSFrançois Tigeot 15935718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_60) 15945718399fSFrançois Tigeot target_refresh = 60; 15955718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_75) 15965718399fSFrançois Tigeot target_refresh = 75; 15975718399fSFrançois Tigeot 15985718399fSFrançois Tigeot preferred_mode = list_first_entry(&connector->probed_modes, 15995718399fSFrançois Tigeot struct drm_display_mode, head); 16005718399fSFrançois Tigeot 16015718399fSFrançois Tigeot list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { 16025718399fSFrançois Tigeot cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; 16035718399fSFrançois Tigeot 16045718399fSFrançois Tigeot if (cur_mode == preferred_mode) 16055718399fSFrançois Tigeot continue; 16065718399fSFrançois Tigeot 16075718399fSFrançois Tigeot /* Largest mode is preferred */ 16085718399fSFrançois Tigeot if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) 16095718399fSFrançois Tigeot preferred_mode = cur_mode; 16105718399fSFrançois Tigeot 16119edbd4a0SFrançois Tigeot cur_vrefresh = cur_mode->vrefresh ? 16129edbd4a0SFrançois Tigeot cur_mode->vrefresh : drm_mode_vrefresh(cur_mode); 16139edbd4a0SFrançois Tigeot preferred_vrefresh = preferred_mode->vrefresh ? 16149edbd4a0SFrançois Tigeot preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode); 16155718399fSFrançois Tigeot /* At a given size, try to get closest to target refresh */ 16165718399fSFrançois Tigeot if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && 16179edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) < 16189edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) { 16195718399fSFrançois Tigeot preferred_mode = cur_mode; 16205718399fSFrançois Tigeot } 16215718399fSFrançois Tigeot } 16225718399fSFrançois Tigeot 16235718399fSFrançois Tigeot preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; 16245718399fSFrançois Tigeot } 16255718399fSFrançois Tigeot 1626ce3d36d7SFrançois Tigeot static bool 1627ce3d36d7SFrançois Tigeot mode_is_rb(const struct drm_display_mode *mode) 1628ce3d36d7SFrançois Tigeot { 1629ce3d36d7SFrançois Tigeot return (mode->htotal - mode->hdisplay == 160) && 1630ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hdisplay == 80) && 1631ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hsync_start == 32) && 1632ce3d36d7SFrançois Tigeot (mode->vsync_start - mode->vdisplay == 3); 1633ce3d36d7SFrançois Tigeot } 1634ce3d36d7SFrançois Tigeot 1635af4b81b9SFrançois Tigeot /* 1636af4b81b9SFrançois Tigeot * drm_mode_find_dmt - Create a copy of a mode if present in DMT 1637af4b81b9SFrançois Tigeot * @dev: Device to duplicate against 1638af4b81b9SFrançois Tigeot * @hsize: Mode width 1639af4b81b9SFrançois Tigeot * @vsize: Mode height 1640af4b81b9SFrançois Tigeot * @fresh: Mode refresh rate 1641af4b81b9SFrançois Tigeot * @rb: Mode reduced-blanking-ness 1642af4b81b9SFrançois Tigeot * 1643af4b81b9SFrançois Tigeot * Walk the DMT mode list looking for a match for the given parameters. 1644ba55f2f5SFrançois Tigeot * 1645ba55f2f5SFrançois Tigeot * Return: A newly allocated copy of the mode, or NULL if not found. 1646af4b81b9SFrançois Tigeot */ 16475718399fSFrançois Tigeot struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, 1648ce3d36d7SFrançois Tigeot int hsize, int vsize, int fresh, 1649ce3d36d7SFrançois Tigeot bool rb) 16505718399fSFrançois Tigeot { 16515718399fSFrançois Tigeot int i; 16525718399fSFrançois Tigeot 1653d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 1654ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 1655ce3d36d7SFrançois Tigeot if (hsize != ptr->hdisplay) 1656ce3d36d7SFrançois Tigeot continue; 1657ce3d36d7SFrançois Tigeot if (vsize != ptr->vdisplay) 1658ce3d36d7SFrançois Tigeot continue; 1659ce3d36d7SFrançois Tigeot if (fresh != drm_mode_vrefresh(ptr)) 1660ce3d36d7SFrançois Tigeot continue; 1661ce3d36d7SFrançois Tigeot if (rb != mode_is_rb(ptr)) 1662ce3d36d7SFrançois Tigeot continue; 1663ce3d36d7SFrançois Tigeot 1664ce3d36d7SFrançois Tigeot return drm_mode_duplicate(dev, ptr); 16655718399fSFrançois Tigeot } 1666ce3d36d7SFrançois Tigeot 1667ce3d36d7SFrançois Tigeot return NULL; 16685718399fSFrançois Tigeot } 1669af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_find_dmt); 16705718399fSFrançois Tigeot 16715718399fSFrançois Tigeot typedef void detailed_cb(struct detailed_timing *timing, void *closure); 16725718399fSFrançois Tigeot 16735718399fSFrançois Tigeot static void 16745718399fSFrançois Tigeot cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 16755718399fSFrançois Tigeot { 16765718399fSFrançois Tigeot int i, n = 0; 1677e3b244c9SFrançois Tigeot u8 d = ext[0x02]; 16785718399fSFrançois Tigeot u8 *det_base = ext + d; 16795718399fSFrançois Tigeot 1680e3b244c9SFrançois Tigeot n = (127 - d) / 18; 16815718399fSFrançois Tigeot for (i = 0; i < n; i++) 16825718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 16835718399fSFrançois Tigeot } 16845718399fSFrançois Tigeot 16855718399fSFrançois Tigeot static void 16865718399fSFrançois Tigeot vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 16875718399fSFrançois Tigeot { 16885718399fSFrançois Tigeot unsigned int i, n = min((int)ext[0x02], 6); 16895718399fSFrançois Tigeot u8 *det_base = ext + 5; 16905718399fSFrançois Tigeot 16915718399fSFrançois Tigeot if (ext[0x01] != 1) 16925718399fSFrançois Tigeot return; /* unknown version */ 16935718399fSFrançois Tigeot 16945718399fSFrançois Tigeot for (i = 0; i < n; i++) 16955718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 16965718399fSFrançois Tigeot } 16975718399fSFrançois Tigeot 16985718399fSFrançois Tigeot static void 16995718399fSFrançois Tigeot drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) 17005718399fSFrançois Tigeot { 17015718399fSFrançois Tigeot int i; 17025718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 17035718399fSFrançois Tigeot 17045718399fSFrançois Tigeot if (edid == NULL) 17055718399fSFrançois Tigeot return; 17065718399fSFrançois Tigeot 17075718399fSFrançois Tigeot for (i = 0; i < EDID_DETAILED_TIMINGS; i++) 17085718399fSFrançois Tigeot cb(&(edid->detailed_timings[i]), closure); 17095718399fSFrançois Tigeot 17105718399fSFrançois Tigeot for (i = 1; i <= raw_edid[0x7e]; i++) { 17115718399fSFrançois Tigeot u8 *ext = raw_edid + (i * EDID_LENGTH); 17125718399fSFrançois Tigeot switch (*ext) { 17135718399fSFrançois Tigeot case CEA_EXT: 17145718399fSFrançois Tigeot cea_for_each_detailed_block(ext, cb, closure); 17155718399fSFrançois Tigeot break; 17165718399fSFrançois Tigeot case VTB_EXT: 17175718399fSFrançois Tigeot vtb_for_each_detailed_block(ext, cb, closure); 17185718399fSFrançois Tigeot break; 17195718399fSFrançois Tigeot default: 17205718399fSFrançois Tigeot break; 17215718399fSFrançois Tigeot } 17225718399fSFrançois Tigeot } 17235718399fSFrançois Tigeot } 17245718399fSFrançois Tigeot 17255718399fSFrançois Tigeot static void 17265718399fSFrançois Tigeot is_rb(struct detailed_timing *t, void *data) 17275718399fSFrançois Tigeot { 17285718399fSFrançois Tigeot u8 *r = (u8 *)t; 17295718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE) 17305718399fSFrançois Tigeot if (r[15] & 0x10) 17315718399fSFrançois Tigeot *(bool *)data = true; 17325718399fSFrançois Tigeot } 17335718399fSFrançois Tigeot 17345718399fSFrançois Tigeot /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ 17355718399fSFrançois Tigeot static bool 17365718399fSFrançois Tigeot drm_monitor_supports_rb(struct edid *edid) 17375718399fSFrançois Tigeot { 17385718399fSFrançois Tigeot if (edid->revision >= 4) { 17396e29dde8SFrançois Tigeot bool ret = false; 17405718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); 17415718399fSFrançois Tigeot return ret; 17425718399fSFrançois Tigeot } 17435718399fSFrançois Tigeot 17445718399fSFrançois Tigeot return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); 17455718399fSFrançois Tigeot } 17465718399fSFrançois Tigeot 17475718399fSFrançois Tigeot static void 17485718399fSFrançois Tigeot find_gtf2(struct detailed_timing *t, void *data) 17495718399fSFrançois Tigeot { 17505718399fSFrançois Tigeot u8 *r = (u8 *)t; 17515718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02) 17525718399fSFrançois Tigeot *(u8 **)data = r; 17535718399fSFrançois Tigeot } 17545718399fSFrançois Tigeot 17555718399fSFrançois Tigeot /* Secondary GTF curve kicks in above some break frequency */ 17565718399fSFrançois Tigeot static int 17575718399fSFrançois Tigeot drm_gtf2_hbreak(struct edid *edid) 17585718399fSFrançois Tigeot { 17595718399fSFrançois Tigeot u8 *r = NULL; 17605718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 17615718399fSFrançois Tigeot return r ? (r[12] * 2) : 0; 17625718399fSFrançois Tigeot } 17635718399fSFrançois Tigeot 17645718399fSFrançois Tigeot static int 17655718399fSFrançois Tigeot drm_gtf2_2c(struct edid *edid) 17665718399fSFrançois Tigeot { 17675718399fSFrançois Tigeot u8 *r = NULL; 17685718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 17695718399fSFrançois Tigeot return r ? r[13] : 0; 17705718399fSFrançois Tigeot } 17715718399fSFrançois Tigeot 17725718399fSFrançois Tigeot static int 17735718399fSFrançois Tigeot drm_gtf2_m(struct edid *edid) 17745718399fSFrançois Tigeot { 17755718399fSFrançois Tigeot u8 *r = NULL; 17765718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 17775718399fSFrançois Tigeot return r ? (r[15] << 8) + r[14] : 0; 17785718399fSFrançois Tigeot } 17795718399fSFrançois Tigeot 17805718399fSFrançois Tigeot static int 17815718399fSFrançois Tigeot drm_gtf2_k(struct edid *edid) 17825718399fSFrançois Tigeot { 17835718399fSFrançois Tigeot u8 *r = NULL; 17845718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 17855718399fSFrançois Tigeot return r ? r[16] : 0; 17865718399fSFrançois Tigeot } 17875718399fSFrançois Tigeot 17885718399fSFrançois Tigeot static int 17895718399fSFrançois Tigeot drm_gtf2_2j(struct edid *edid) 17905718399fSFrançois Tigeot { 17915718399fSFrançois Tigeot u8 *r = NULL; 17925718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 17935718399fSFrançois Tigeot return r ? r[17] : 0; 17945718399fSFrançois Tigeot } 17955718399fSFrançois Tigeot 17965718399fSFrançois Tigeot /** 17975718399fSFrançois Tigeot * standard_timing_level - get std. timing level(CVT/GTF/DMT) 17985718399fSFrançois Tigeot * @edid: EDID block to scan 17995718399fSFrançois Tigeot */ 18005718399fSFrançois Tigeot static int standard_timing_level(struct edid *edid) 18015718399fSFrançois Tigeot { 18025718399fSFrançois Tigeot if (edid->revision >= 2) { 18035718399fSFrançois Tigeot if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) 18045718399fSFrançois Tigeot return LEVEL_CVT; 18055718399fSFrançois Tigeot if (drm_gtf2_hbreak(edid)) 18065718399fSFrançois Tigeot return LEVEL_GTF2; 18075718399fSFrançois Tigeot return LEVEL_GTF; 18085718399fSFrançois Tigeot } 18095718399fSFrançois Tigeot return LEVEL_DMT; 18105718399fSFrançois Tigeot } 18115718399fSFrançois Tigeot 18125718399fSFrançois Tigeot /* 18135718399fSFrançois Tigeot * 0 is reserved. The spec says 0x01 fill for unused timings. Some old 18145718399fSFrançois Tigeot * monitors fill with ascii space (0x20) instead. 18155718399fSFrançois Tigeot */ 18165718399fSFrançois Tigeot static int 18175718399fSFrançois Tigeot bad_std_timing(u8 a, u8 b) 18185718399fSFrançois Tigeot { 18195718399fSFrançois Tigeot return (a == 0x00 && b == 0x00) || 18205718399fSFrançois Tigeot (a == 0x01 && b == 0x01) || 18215718399fSFrançois Tigeot (a == 0x20 && b == 0x20); 18225718399fSFrançois Tigeot } 18235718399fSFrançois Tigeot 18245718399fSFrançois Tigeot /** 18255718399fSFrançois Tigeot * drm_mode_std - convert standard mode info (width, height, refresh) into mode 1826ba55f2f5SFrançois Tigeot * @connector: connector of for the EDID block 1827ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 18285718399fSFrançois Tigeot * @t: standard timing params 18295718399fSFrançois Tigeot * 18305718399fSFrançois Tigeot * Take the standard timing params (in this case width, aspect, and refresh) 18315718399fSFrançois Tigeot * and convert them into a real mode using CVT/GTF/DMT. 18325718399fSFrançois Tigeot */ 18335718399fSFrançois Tigeot static struct drm_display_mode * 18345718399fSFrançois Tigeot drm_mode_std(struct drm_connector *connector, struct edid *edid, 1835ba55f2f5SFrançois Tigeot struct std_timing *t) 18365718399fSFrançois Tigeot { 18375718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 18385718399fSFrançois Tigeot struct drm_display_mode *m, *mode = NULL; 18395718399fSFrançois Tigeot int hsize, vsize; 18405718399fSFrançois Tigeot int vrefresh_rate; 18415718399fSFrançois Tigeot unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) 18425718399fSFrançois Tigeot >> EDID_TIMING_ASPECT_SHIFT; 18435718399fSFrançois Tigeot unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) 18445718399fSFrançois Tigeot >> EDID_TIMING_VFREQ_SHIFT; 18455718399fSFrançois Tigeot int timing_level = standard_timing_level(edid); 18465718399fSFrançois Tigeot 18475718399fSFrançois Tigeot if (bad_std_timing(t->hsize, t->vfreq_aspect)) 18485718399fSFrançois Tigeot return NULL; 18495718399fSFrançois Tigeot 18505718399fSFrançois Tigeot /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ 18515718399fSFrançois Tigeot hsize = t->hsize * 8 + 248; 18525718399fSFrançois Tigeot /* vrefresh_rate = vfreq + 60 */ 18535718399fSFrançois Tigeot vrefresh_rate = vfreq + 60; 18545718399fSFrançois Tigeot /* the vdisplay is calculated based on the aspect ratio */ 18555718399fSFrançois Tigeot if (aspect_ratio == 0) { 1856ba55f2f5SFrançois Tigeot if (edid->revision < 3) 18575718399fSFrançois Tigeot vsize = hsize; 18585718399fSFrançois Tigeot else 18595718399fSFrançois Tigeot vsize = (hsize * 10) / 16; 18605718399fSFrançois Tigeot } else if (aspect_ratio == 1) 18615718399fSFrançois Tigeot vsize = (hsize * 3) / 4; 18625718399fSFrançois Tigeot else if (aspect_ratio == 2) 18635718399fSFrançois Tigeot vsize = (hsize * 4) / 5; 18645718399fSFrançois Tigeot else 18655718399fSFrançois Tigeot vsize = (hsize * 9) / 16; 18665718399fSFrançois Tigeot 18675718399fSFrançois Tigeot /* HDTV hack, part 1 */ 18685718399fSFrançois Tigeot if (vrefresh_rate == 60 && 18695718399fSFrançois Tigeot ((hsize == 1360 && vsize == 765) || 18705718399fSFrançois Tigeot (hsize == 1368 && vsize == 769))) { 18715718399fSFrançois Tigeot hsize = 1366; 18725718399fSFrançois Tigeot vsize = 768; 18735718399fSFrançois Tigeot } 18745718399fSFrançois Tigeot 18755718399fSFrançois Tigeot /* 18765718399fSFrançois Tigeot * If this connector already has a mode for this size and refresh 18775718399fSFrançois Tigeot * rate (because it came from detailed or CVT info), use that 18785718399fSFrançois Tigeot * instead. This way we don't have to guess at interlace or 18795718399fSFrançois Tigeot * reduced blanking. 18805718399fSFrançois Tigeot */ 18815718399fSFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) 18825718399fSFrançois Tigeot if (m->hdisplay == hsize && m->vdisplay == vsize && 18835718399fSFrançois Tigeot drm_mode_vrefresh(m) == vrefresh_rate) 18845718399fSFrançois Tigeot return NULL; 18855718399fSFrançois Tigeot 18865718399fSFrançois Tigeot /* HDTV hack, part 2 */ 18875718399fSFrançois Tigeot if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) { 18885718399fSFrançois Tigeot mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0, 18895718399fSFrançois Tigeot false); 18905718399fSFrançois Tigeot mode->hdisplay = 1366; 18915718399fSFrançois Tigeot mode->hsync_start = mode->hsync_start - 1; 18925718399fSFrançois Tigeot mode->hsync_end = mode->hsync_end - 1; 18935718399fSFrançois Tigeot return mode; 18945718399fSFrançois Tigeot } 18955718399fSFrançois Tigeot 18965718399fSFrançois Tigeot /* check whether it can be found in default mode table */ 1897ce3d36d7SFrançois Tigeot if (drm_monitor_supports_rb(edid)) { 1898ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, 1899ce3d36d7SFrançois Tigeot true); 1900ce3d36d7SFrançois Tigeot if (mode) 1901ce3d36d7SFrançois Tigeot return mode; 1902ce3d36d7SFrançois Tigeot } 1903ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false); 19045718399fSFrançois Tigeot if (mode) 19055718399fSFrançois Tigeot return mode; 19065718399fSFrançois Tigeot 1907ce3d36d7SFrançois Tigeot /* okay, generate it */ 19085718399fSFrançois Tigeot switch (timing_level) { 19095718399fSFrançois Tigeot case LEVEL_DMT: 19105718399fSFrançois Tigeot break; 19115718399fSFrançois Tigeot case LEVEL_GTF: 19125718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 19135718399fSFrançois Tigeot break; 19145718399fSFrançois Tigeot case LEVEL_GTF2: 19155718399fSFrançois Tigeot /* 19165718399fSFrançois Tigeot * This is potentially wrong if there's ever a monitor with 19175718399fSFrançois Tigeot * more than one ranges section, each claiming a different 19185718399fSFrançois Tigeot * secondary GTF curve. Please don't do that. 19195718399fSFrançois Tigeot */ 19205718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 1921ce3d36d7SFrançois Tigeot if (!mode) 1922ce3d36d7SFrançois Tigeot return NULL; 19235718399fSFrançois Tigeot if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { 1924ce3d36d7SFrançois Tigeot drm_mode_destroy(dev, mode); 19255718399fSFrançois Tigeot mode = drm_gtf_mode_complex(dev, hsize, vsize, 19265718399fSFrançois Tigeot vrefresh_rate, 0, 0, 19275718399fSFrançois Tigeot drm_gtf2_m(edid), 19285718399fSFrançois Tigeot drm_gtf2_2c(edid), 19295718399fSFrançois Tigeot drm_gtf2_k(edid), 19305718399fSFrançois Tigeot drm_gtf2_2j(edid)); 19315718399fSFrançois Tigeot } 19325718399fSFrançois Tigeot break; 19335718399fSFrançois Tigeot case LEVEL_CVT: 19345718399fSFrançois Tigeot mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, 19355718399fSFrançois Tigeot false); 19365718399fSFrançois Tigeot break; 19375718399fSFrançois Tigeot } 19385718399fSFrançois Tigeot return mode; 19395718399fSFrançois Tigeot } 19405718399fSFrançois Tigeot 19415718399fSFrançois Tigeot /* 19425718399fSFrançois Tigeot * EDID is delightfully ambiguous about how interlaced modes are to be 19435718399fSFrançois Tigeot * encoded. Our internal representation is of frame height, but some 19445718399fSFrançois Tigeot * HDTV detailed timings are encoded as field height. 19455718399fSFrançois Tigeot * 19465718399fSFrançois Tigeot * The format list here is from CEA, in frame size. Technically we 19475718399fSFrançois Tigeot * should be checking refresh rate too. Whatever. 19485718399fSFrançois Tigeot */ 19495718399fSFrançois Tigeot static void 19505718399fSFrançois Tigeot drm_mode_do_interlace_quirk(struct drm_display_mode *mode, 19515718399fSFrançois Tigeot struct detailed_pixel_timing *pt) 19525718399fSFrançois Tigeot { 19535718399fSFrançois Tigeot int i; 19545718399fSFrançois Tigeot static const struct { 19555718399fSFrançois Tigeot int w, h; 19565718399fSFrançois Tigeot } cea_interlaced[] = { 19575718399fSFrançois Tigeot { 1920, 1080 }, 19585718399fSFrançois Tigeot { 720, 480 }, 19595718399fSFrançois Tigeot { 1440, 480 }, 19605718399fSFrançois Tigeot { 2880, 480 }, 19615718399fSFrançois Tigeot { 720, 576 }, 19625718399fSFrançois Tigeot { 1440, 576 }, 19635718399fSFrançois Tigeot { 2880, 576 }, 19645718399fSFrançois Tigeot }; 19655718399fSFrançois Tigeot 19665718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_INTERLACED)) 19675718399fSFrançois Tigeot return; 19685718399fSFrançois Tigeot 19696e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) { 19705718399fSFrançois Tigeot if ((mode->hdisplay == cea_interlaced[i].w) && 19715718399fSFrançois Tigeot (mode->vdisplay == cea_interlaced[i].h / 2)) { 19725718399fSFrançois Tigeot mode->vdisplay *= 2; 19735718399fSFrançois Tigeot mode->vsync_start *= 2; 19745718399fSFrançois Tigeot mode->vsync_end *= 2; 19755718399fSFrançois Tigeot mode->vtotal *= 2; 19765718399fSFrançois Tigeot mode->vtotal |= 1; 19775718399fSFrançois Tigeot } 19785718399fSFrançois Tigeot } 19795718399fSFrançois Tigeot 19805718399fSFrançois Tigeot mode->flags |= DRM_MODE_FLAG_INTERLACE; 19815718399fSFrançois Tigeot } 19825718399fSFrançois Tigeot 19835718399fSFrançois Tigeot /** 19845718399fSFrançois Tigeot * drm_mode_detailed - create a new mode from an EDID detailed timing section 19855718399fSFrançois Tigeot * @dev: DRM device (needed to create new mode) 19865718399fSFrançois Tigeot * @edid: EDID block 19875718399fSFrançois Tigeot * @timing: EDID detailed timing info 19885718399fSFrançois Tigeot * @quirks: quirks to apply 19895718399fSFrançois Tigeot * 19905718399fSFrançois Tigeot * An EDID detailed timing block contains enough info for us to create and 19915718399fSFrançois Tigeot * return a new struct drm_display_mode. 19925718399fSFrançois Tigeot */ 19935718399fSFrançois Tigeot static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, 19945718399fSFrançois Tigeot struct edid *edid, 19955718399fSFrançois Tigeot struct detailed_timing *timing, 19965718399fSFrançois Tigeot u32 quirks) 19975718399fSFrançois Tigeot { 19985718399fSFrançois Tigeot struct drm_display_mode *mode; 19995718399fSFrançois Tigeot struct detailed_pixel_timing *pt = &timing->data.pixel_data; 20005718399fSFrançois Tigeot unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; 20015718399fSFrançois Tigeot unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; 20025718399fSFrançois Tigeot unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; 20035718399fSFrançois Tigeot unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; 20045718399fSFrançois Tigeot unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; 20055718399fSFrançois Tigeot unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; 2006d82bf20eSFrançois Tigeot unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4; 20075718399fSFrançois Tigeot unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); 20085718399fSFrançois Tigeot 20095718399fSFrançois Tigeot /* ignore tiny modes */ 20105718399fSFrançois Tigeot if (hactive < 64 || vactive < 64) 20115718399fSFrançois Tigeot return NULL; 20125718399fSFrançois Tigeot 20135718399fSFrançois Tigeot if (pt->misc & DRM_EDID_PT_STEREO) { 20149edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("stereo mode not supported\n"); 20155718399fSFrançois Tigeot return NULL; 20165718399fSFrançois Tigeot } 20175718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { 20189edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("composite sync not supported\n"); 20195718399fSFrançois Tigeot } 20205718399fSFrançois Tigeot 20215718399fSFrançois Tigeot /* it is incorrect if hsync/vsync width is zero */ 20225718399fSFrançois Tigeot if (!hsync_pulse_width || !vsync_pulse_width) { 20235718399fSFrançois Tigeot DRM_DEBUG_KMS("Incorrect Detailed timing. " 20245718399fSFrançois Tigeot "Wrong Hsync/Vsync pulse width\n"); 20255718399fSFrançois Tigeot return NULL; 20265718399fSFrançois Tigeot } 20276e29dde8SFrançois Tigeot 20286e29dde8SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { 20296e29dde8SFrançois Tigeot mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); 20306e29dde8SFrançois Tigeot if (!mode) 20316e29dde8SFrançois Tigeot return NULL; 20326e29dde8SFrançois Tigeot 20336e29dde8SFrançois Tigeot goto set_size; 20346e29dde8SFrançois Tigeot } 20356e29dde8SFrançois Tigeot 20365718399fSFrançois Tigeot mode = drm_mode_create(dev); 20375718399fSFrançois Tigeot if (!mode) 20385718399fSFrançois Tigeot return NULL; 20395718399fSFrançois Tigeot 20405718399fSFrançois Tigeot if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) 20416e29dde8SFrançois Tigeot timing->pixel_clock = cpu_to_le16(1088); 20425718399fSFrançois Tigeot 20436e29dde8SFrançois Tigeot mode->clock = le16_to_cpu(timing->pixel_clock) * 10; 20445718399fSFrançois Tigeot 20455718399fSFrançois Tigeot mode->hdisplay = hactive; 20465718399fSFrançois Tigeot mode->hsync_start = mode->hdisplay + hsync_offset; 20475718399fSFrançois Tigeot mode->hsync_end = mode->hsync_start + hsync_pulse_width; 20485718399fSFrançois Tigeot mode->htotal = mode->hdisplay + hblank; 20495718399fSFrançois Tigeot 20505718399fSFrançois Tigeot mode->vdisplay = vactive; 20515718399fSFrançois Tigeot mode->vsync_start = mode->vdisplay + vsync_offset; 20525718399fSFrançois Tigeot mode->vsync_end = mode->vsync_start + vsync_pulse_width; 20535718399fSFrançois Tigeot mode->vtotal = mode->vdisplay + vblank; 20545718399fSFrançois Tigeot 20555718399fSFrançois Tigeot /* Some EDIDs have bogus h/vtotal values */ 20565718399fSFrançois Tigeot if (mode->hsync_end > mode->htotal) 20575718399fSFrançois Tigeot mode->htotal = mode->hsync_end + 1; 20585718399fSFrançois Tigeot if (mode->vsync_end > mode->vtotal) 20595718399fSFrançois Tigeot mode->vtotal = mode->vsync_end + 1; 20605718399fSFrançois Tigeot 20615718399fSFrançois Tigeot drm_mode_do_interlace_quirk(mode, pt); 20625718399fSFrançois Tigeot 20635718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { 20645718399fSFrançois Tigeot pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; 20655718399fSFrançois Tigeot } 20665718399fSFrançois Tigeot 20675718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? 20685718399fSFrançois Tigeot DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 20695718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? 20705718399fSFrançois Tigeot DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 20715718399fSFrançois Tigeot 20726e29dde8SFrançois Tigeot set_size: 20735718399fSFrançois Tigeot mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; 20745718399fSFrançois Tigeot mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; 20755718399fSFrançois Tigeot 20765718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_IN_CM) { 20775718399fSFrançois Tigeot mode->width_mm *= 10; 20785718399fSFrançois Tigeot mode->height_mm *= 10; 20795718399fSFrançois Tigeot } 20805718399fSFrançois Tigeot 20815718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { 20825718399fSFrançois Tigeot mode->width_mm = edid->width_cm * 10; 20835718399fSFrançois Tigeot mode->height_mm = edid->height_cm * 10; 20845718399fSFrançois Tigeot } 20855718399fSFrançois Tigeot 20866e29dde8SFrançois Tigeot mode->type = DRM_MODE_TYPE_DRIVER; 2087d82bf20eSFrançois Tigeot mode->vrefresh = drm_mode_vrefresh(mode); 20886e29dde8SFrançois Tigeot drm_mode_set_name(mode); 20896e29dde8SFrançois Tigeot 20905718399fSFrançois Tigeot return mode; 20915718399fSFrançois Tigeot } 20925718399fSFrançois Tigeot 20935718399fSFrançois Tigeot static bool 2094ce3d36d7SFrançois Tigeot mode_in_hsync_range(const struct drm_display_mode *mode, 20955718399fSFrançois Tigeot struct edid *edid, u8 *t) 20965718399fSFrançois Tigeot { 20975718399fSFrançois Tigeot int hsync, hmin, hmax; 20985718399fSFrançois Tigeot 20995718399fSFrançois Tigeot hmin = t[7]; 21005718399fSFrançois Tigeot if (edid->revision >= 4) 21015718399fSFrançois Tigeot hmin += ((t[4] & 0x04) ? 255 : 0); 21025718399fSFrançois Tigeot hmax = t[8]; 21035718399fSFrançois Tigeot if (edid->revision >= 4) 21045718399fSFrançois Tigeot hmax += ((t[4] & 0x08) ? 255 : 0); 21055718399fSFrançois Tigeot hsync = drm_mode_hsync(mode); 21065718399fSFrançois Tigeot 21075718399fSFrançois Tigeot return (hsync <= hmax && hsync >= hmin); 21085718399fSFrançois Tigeot } 21095718399fSFrançois Tigeot 21105718399fSFrançois Tigeot static bool 2111ce3d36d7SFrançois Tigeot mode_in_vsync_range(const struct drm_display_mode *mode, 21125718399fSFrançois Tigeot struct edid *edid, u8 *t) 21135718399fSFrançois Tigeot { 21145718399fSFrançois Tigeot int vsync, vmin, vmax; 21155718399fSFrançois Tigeot 21165718399fSFrançois Tigeot vmin = t[5]; 21175718399fSFrançois Tigeot if (edid->revision >= 4) 21185718399fSFrançois Tigeot vmin += ((t[4] & 0x01) ? 255 : 0); 21195718399fSFrançois Tigeot vmax = t[6]; 21205718399fSFrançois Tigeot if (edid->revision >= 4) 21215718399fSFrançois Tigeot vmax += ((t[4] & 0x02) ? 255 : 0); 21225718399fSFrançois Tigeot vsync = drm_mode_vrefresh(mode); 21235718399fSFrançois Tigeot 21245718399fSFrançois Tigeot return (vsync <= vmax && vsync >= vmin); 21255718399fSFrançois Tigeot } 21265718399fSFrançois Tigeot 21275718399fSFrançois Tigeot static u32 21285718399fSFrançois Tigeot range_pixel_clock(struct edid *edid, u8 *t) 21295718399fSFrançois Tigeot { 21305718399fSFrançois Tigeot /* unspecified */ 21315718399fSFrançois Tigeot if (t[9] == 0 || t[9] == 255) 21325718399fSFrançois Tigeot return 0; 21335718399fSFrançois Tigeot 21345718399fSFrançois Tigeot /* 1.4 with CVT support gives us real precision, yay */ 21355718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 21365718399fSFrançois Tigeot return (t[9] * 10000) - ((t[12] >> 2) * 250); 21375718399fSFrançois Tigeot 21385718399fSFrançois Tigeot /* 1.3 is pathetic, so fuzz up a bit */ 21395718399fSFrançois Tigeot return t[9] * 10000 + 5001; 21405718399fSFrançois Tigeot } 21415718399fSFrançois Tigeot 21425718399fSFrançois Tigeot static bool 2143ce3d36d7SFrançois Tigeot mode_in_range(const struct drm_display_mode *mode, struct edid *edid, 21445718399fSFrançois Tigeot struct detailed_timing *timing) 21455718399fSFrançois Tigeot { 21465718399fSFrançois Tigeot u32 max_clock; 21475718399fSFrançois Tigeot u8 *t = (u8 *)timing; 21485718399fSFrançois Tigeot 21495718399fSFrançois Tigeot if (!mode_in_hsync_range(mode, edid, t)) 21505718399fSFrançois Tigeot return false; 21515718399fSFrançois Tigeot 21525718399fSFrançois Tigeot if (!mode_in_vsync_range(mode, edid, t)) 21535718399fSFrançois Tigeot return false; 21545718399fSFrançois Tigeot 21555718399fSFrançois Tigeot if ((max_clock = range_pixel_clock(edid, t))) 21565718399fSFrançois Tigeot if (mode->clock > max_clock) 21575718399fSFrançois Tigeot return false; 21585718399fSFrançois Tigeot 21595718399fSFrançois Tigeot /* 1.4 max horizontal check */ 21605718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 21615718399fSFrançois Tigeot if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) 21625718399fSFrançois Tigeot return false; 21635718399fSFrançois Tigeot 21645718399fSFrançois Tigeot if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) 21655718399fSFrançois Tigeot return false; 21665718399fSFrançois Tigeot 21675718399fSFrançois Tigeot return true; 21685718399fSFrançois Tigeot } 21695718399fSFrançois Tigeot 21706e29dde8SFrançois Tigeot static bool valid_inferred_mode(const struct drm_connector *connector, 21716e29dde8SFrançois Tigeot const struct drm_display_mode *mode) 21726e29dde8SFrançois Tigeot { 21736e29dde8SFrançois Tigeot struct drm_display_mode *m; 21746e29dde8SFrançois Tigeot bool ok = false; 21756e29dde8SFrançois Tigeot 21766e29dde8SFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) { 21776e29dde8SFrançois Tigeot if (mode->hdisplay == m->hdisplay && 21786e29dde8SFrançois Tigeot mode->vdisplay == m->vdisplay && 21796e29dde8SFrançois Tigeot drm_mode_vrefresh(mode) == drm_mode_vrefresh(m)) 21806e29dde8SFrançois Tigeot return false; /* duplicated */ 21816e29dde8SFrançois Tigeot if (mode->hdisplay <= m->hdisplay && 21826e29dde8SFrançois Tigeot mode->vdisplay <= m->vdisplay) 21836e29dde8SFrançois Tigeot ok = true; 21846e29dde8SFrançois Tigeot } 21856e29dde8SFrançois Tigeot return ok; 21866e29dde8SFrançois Tigeot } 21876e29dde8SFrançois Tigeot 21885718399fSFrançois Tigeot static int 21896e29dde8SFrançois Tigeot drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, 21905718399fSFrançois Tigeot struct detailed_timing *timing) 21915718399fSFrançois Tigeot { 21925718399fSFrançois Tigeot int i, modes = 0; 21935718399fSFrançois Tigeot struct drm_display_mode *newmode; 21945718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 21955718399fSFrançois Tigeot 2196d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 21976e29dde8SFrançois Tigeot if (mode_in_range(drm_dmt_modes + i, edid, timing) && 21986e29dde8SFrançois Tigeot valid_inferred_mode(connector, drm_dmt_modes + i)) { 21995718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); 22005718399fSFrançois Tigeot if (newmode) { 22015718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22025718399fSFrançois Tigeot modes++; 22035718399fSFrançois Tigeot } 22045718399fSFrançois Tigeot } 22055718399fSFrançois Tigeot } 22065718399fSFrançois Tigeot 22075718399fSFrançois Tigeot return modes; 22085718399fSFrançois Tigeot } 22095718399fSFrançois Tigeot 22106e29dde8SFrançois Tigeot /* fix up 1366x768 mode from 1368x768; 22116e29dde8SFrançois Tigeot * GFT/CVT can't express 1366 width which isn't dividable by 8 22126e29dde8SFrançois Tigeot */ 22136e29dde8SFrançois Tigeot static void fixup_mode_1366x768(struct drm_display_mode *mode) 22146e29dde8SFrançois Tigeot { 22156e29dde8SFrançois Tigeot if (mode->hdisplay == 1368 && mode->vdisplay == 768) { 22166e29dde8SFrançois Tigeot mode->hdisplay = 1366; 22176e29dde8SFrançois Tigeot mode->hsync_start--; 22186e29dde8SFrançois Tigeot mode->hsync_end--; 22196e29dde8SFrançois Tigeot drm_mode_set_name(mode); 22206e29dde8SFrançois Tigeot } 22216e29dde8SFrançois Tigeot } 22226e29dde8SFrançois Tigeot 22236e29dde8SFrançois Tigeot static int 22246e29dde8SFrançois Tigeot drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, 22256e29dde8SFrançois Tigeot struct detailed_timing *timing) 22266e29dde8SFrançois Tigeot { 22276e29dde8SFrançois Tigeot int i, modes = 0; 22286e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 22296e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 22306e29dde8SFrançois Tigeot 2231d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 22326e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 22336e29dde8SFrançois Tigeot newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0); 22346e29dde8SFrançois Tigeot if (!newmode) 22356e29dde8SFrançois Tigeot return modes; 22366e29dde8SFrançois Tigeot 22376e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 22386e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 22396e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 22406e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 22416e29dde8SFrançois Tigeot continue; 22426e29dde8SFrançois Tigeot } 22436e29dde8SFrançois Tigeot 22446e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 22456e29dde8SFrançois Tigeot modes++; 22466e29dde8SFrançois Tigeot } 22476e29dde8SFrançois Tigeot 22486e29dde8SFrançois Tigeot return modes; 22496e29dde8SFrançois Tigeot } 22506e29dde8SFrançois Tigeot 22516e29dde8SFrançois Tigeot static int 22526e29dde8SFrançois Tigeot drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, 22536e29dde8SFrançois Tigeot struct detailed_timing *timing) 22546e29dde8SFrançois Tigeot { 22556e29dde8SFrançois Tigeot int i, modes = 0; 22566e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 22576e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 22586e29dde8SFrançois Tigeot bool rb = drm_monitor_supports_rb(edid); 22596e29dde8SFrançois Tigeot 2260d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 22616e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 22626e29dde8SFrançois Tigeot newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0); 22636e29dde8SFrançois Tigeot if (!newmode) 22646e29dde8SFrançois Tigeot return modes; 22656e29dde8SFrançois Tigeot 22666e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 22676e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 22686e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 22696e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 22706e29dde8SFrançois Tigeot continue; 22716e29dde8SFrançois Tigeot } 22726e29dde8SFrançois Tigeot 22736e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 22746e29dde8SFrançois Tigeot modes++; 22756e29dde8SFrançois Tigeot } 22766e29dde8SFrançois Tigeot 22776e29dde8SFrançois Tigeot return modes; 22786e29dde8SFrançois Tigeot } 22796e29dde8SFrançois Tigeot 22805718399fSFrançois Tigeot static void 22815718399fSFrançois Tigeot do_inferred_modes(struct detailed_timing *timing, void *c) 22825718399fSFrançois Tigeot { 22835718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 22845718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 22856e29dde8SFrançois Tigeot struct detailed_data_monitor_range *range = &data->data.range; 22865718399fSFrançois Tigeot 22876e29dde8SFrançois Tigeot if (data->type != EDID_DETAIL_MONITOR_RANGE) 22886e29dde8SFrançois Tigeot return; 22896e29dde8SFrançois Tigeot 22906e29dde8SFrançois Tigeot closure->modes += drm_dmt_modes_for_range(closure->connector, 22916e29dde8SFrançois Tigeot closure->edid, 22926e29dde8SFrançois Tigeot timing); 22936e29dde8SFrançois Tigeot 22946e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 1)) 22956e29dde8SFrançois Tigeot return; /* GTF not defined yet */ 22966e29dde8SFrançois Tigeot 22976e29dde8SFrançois Tigeot switch (range->flags) { 22986e29dde8SFrançois Tigeot case 0x02: /* secondary gtf, XXX could do more */ 22996e29dde8SFrançois Tigeot case 0x00: /* default gtf */ 23005718399fSFrançois Tigeot closure->modes += drm_gtf_modes_for_range(closure->connector, 23015718399fSFrançois Tigeot closure->edid, 23025718399fSFrançois Tigeot timing); 23036e29dde8SFrançois Tigeot break; 23046e29dde8SFrançois Tigeot case 0x04: /* cvt, only in 1.4+ */ 23056e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 3)) 23066e29dde8SFrançois Tigeot break; 23076e29dde8SFrançois Tigeot 23086e29dde8SFrançois Tigeot closure->modes += drm_cvt_modes_for_range(closure->connector, 23096e29dde8SFrançois Tigeot closure->edid, 23106e29dde8SFrançois Tigeot timing); 23116e29dde8SFrançois Tigeot break; 23126e29dde8SFrançois Tigeot case 0x01: /* just the ranges, no formula */ 23136e29dde8SFrançois Tigeot default: 23146e29dde8SFrançois Tigeot break; 23156e29dde8SFrançois Tigeot } 23165718399fSFrançois Tigeot } 23175718399fSFrançois Tigeot 23185718399fSFrançois Tigeot static int 23195718399fSFrançois Tigeot add_inferred_modes(struct drm_connector *connector, struct edid *edid) 23205718399fSFrançois Tigeot { 23215718399fSFrançois Tigeot struct detailed_mode_closure closure = { 232255a9fa67Szrj .connector = connector, 232355a9fa67Szrj .edid = edid, 23245718399fSFrançois Tigeot }; 23255718399fSFrançois Tigeot 23265718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 23275718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, 23285718399fSFrançois Tigeot &closure); 23295718399fSFrançois Tigeot 23305718399fSFrançois Tigeot return closure.modes; 23315718399fSFrançois Tigeot } 23325718399fSFrançois Tigeot 23335718399fSFrançois Tigeot static int 23345718399fSFrançois Tigeot drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) 23355718399fSFrançois Tigeot { 23365718399fSFrançois Tigeot int i, j, m, modes = 0; 23375718399fSFrançois Tigeot struct drm_display_mode *mode; 2338c0e85e96SFrançois Tigeot u8 *est = ((u8 *)timing) + 6; 23395718399fSFrançois Tigeot 23405718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 23419edbd4a0SFrançois Tigeot for (j = 7; j >= 0; j--) { 23425718399fSFrançois Tigeot m = (i * 8) + (7 - j); 2343ce3d36d7SFrançois Tigeot if (m >= ARRAY_SIZE(est3_modes)) 23445718399fSFrançois Tigeot break; 23455718399fSFrançois Tigeot if (est[i] & (1 << j)) { 23465718399fSFrançois Tigeot mode = drm_mode_find_dmt(connector->dev, 23475718399fSFrançois Tigeot est3_modes[m].w, 23485718399fSFrançois Tigeot est3_modes[m].h, 2349ce3d36d7SFrançois Tigeot est3_modes[m].r, 2350ce3d36d7SFrançois Tigeot est3_modes[m].rb); 23515718399fSFrançois Tigeot if (mode) { 23525718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 23535718399fSFrançois Tigeot modes++; 23545718399fSFrançois Tigeot } 23555718399fSFrançois Tigeot } 23565718399fSFrançois Tigeot } 23575718399fSFrançois Tigeot } 23585718399fSFrançois Tigeot 23595718399fSFrançois Tigeot return modes; 23605718399fSFrançois Tigeot } 23615718399fSFrançois Tigeot 23625718399fSFrançois Tigeot static void 23635718399fSFrançois Tigeot do_established_modes(struct detailed_timing *timing, void *c) 23645718399fSFrançois Tigeot { 23655718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 23665718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 23675718399fSFrançois Tigeot 23685718399fSFrançois Tigeot if (data->type == EDID_DETAIL_EST_TIMINGS) 23695718399fSFrançois Tigeot closure->modes += drm_est3_modes(closure->connector, timing); 23705718399fSFrançois Tigeot } 23715718399fSFrançois Tigeot 23725718399fSFrançois Tigeot /** 23735718399fSFrançois Tigeot * add_established_modes - get est. modes from EDID and add them 2374ba55f2f5SFrançois Tigeot * @connector: connector to add mode(s) to 23755718399fSFrançois Tigeot * @edid: EDID block to scan 23765718399fSFrançois Tigeot * 23775718399fSFrançois Tigeot * Each EDID block contains a bitmap of the supported "established modes" list 23785718399fSFrançois Tigeot * (defined above). Tease them out and add them to the global modes list. 23795718399fSFrançois Tigeot */ 23805718399fSFrançois Tigeot static int 23815718399fSFrançois Tigeot add_established_modes(struct drm_connector *connector, struct edid *edid) 23825718399fSFrançois Tigeot { 23835718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 23845718399fSFrançois Tigeot unsigned long est_bits = edid->established_timings.t1 | 23855718399fSFrançois Tigeot (edid->established_timings.t2 << 8) | 23865718399fSFrançois Tigeot ((edid->established_timings.mfg_rsvd & 0x80) << 9); 23875718399fSFrançois Tigeot int i, modes = 0; 23885718399fSFrançois Tigeot struct detailed_mode_closure closure = { 238955a9fa67Szrj .connector = connector, 239055a9fa67Szrj .edid = edid, 23915718399fSFrançois Tigeot }; 23925718399fSFrançois Tigeot 23935718399fSFrançois Tigeot for (i = 0; i <= EDID_EST_TIMINGS; i++) { 23945718399fSFrançois Tigeot if (est_bits & (1<<i)) { 23955718399fSFrançois Tigeot struct drm_display_mode *newmode; 23965718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); 23975718399fSFrançois Tigeot if (newmode) { 23985718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 23995718399fSFrançois Tigeot modes++; 24005718399fSFrançois Tigeot } 24015718399fSFrançois Tigeot } 24025718399fSFrançois Tigeot } 24035718399fSFrançois Tigeot 24045718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 24055718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, 24065718399fSFrançois Tigeot do_established_modes, &closure); 24075718399fSFrançois Tigeot 24085718399fSFrançois Tigeot return modes + closure.modes; 24095718399fSFrançois Tigeot } 24105718399fSFrançois Tigeot 24115718399fSFrançois Tigeot static void 24125718399fSFrançois Tigeot do_standard_modes(struct detailed_timing *timing, void *c) 24135718399fSFrançois Tigeot { 24145718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 24155718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 24165718399fSFrançois Tigeot struct drm_connector *connector = closure->connector; 24175718399fSFrançois Tigeot struct edid *edid = closure->edid; 24185718399fSFrançois Tigeot 24195718399fSFrançois Tigeot if (data->type == EDID_DETAIL_STD_MODES) { 24205718399fSFrançois Tigeot int i; 24215718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 24225718399fSFrançois Tigeot struct std_timing *std; 24235718399fSFrançois Tigeot struct drm_display_mode *newmode; 24245718399fSFrançois Tigeot 24255718399fSFrançois Tigeot std = &data->data.timings[i]; 2426ba55f2f5SFrançois Tigeot newmode = drm_mode_std(connector, edid, std); 24275718399fSFrançois Tigeot if (newmode) { 24285718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 24295718399fSFrançois Tigeot closure->modes++; 24305718399fSFrançois Tigeot } 24315718399fSFrançois Tigeot } 24325718399fSFrançois Tigeot } 24335718399fSFrançois Tigeot } 24345718399fSFrançois Tigeot 24355718399fSFrançois Tigeot /** 24365718399fSFrançois Tigeot * add_standard_modes - get std. modes from EDID and add them 2437ba55f2f5SFrançois Tigeot * @connector: connector to add mode(s) to 24385718399fSFrançois Tigeot * @edid: EDID block to scan 24395718399fSFrançois Tigeot * 24405718399fSFrançois Tigeot * Standard modes can be calculated using the appropriate standard (DMT, 24415718399fSFrançois Tigeot * GTF or CVT. Grab them from @edid and add them to the list. 24425718399fSFrançois Tigeot */ 24435718399fSFrançois Tigeot static int 24445718399fSFrançois Tigeot add_standard_modes(struct drm_connector *connector, struct edid *edid) 24455718399fSFrançois Tigeot { 24465718399fSFrançois Tigeot int i, modes = 0; 24475718399fSFrançois Tigeot struct detailed_mode_closure closure = { 244855a9fa67Szrj .connector = connector, 244955a9fa67Szrj .edid = edid, 24505718399fSFrançois Tigeot }; 24515718399fSFrançois Tigeot 24525718399fSFrançois Tigeot for (i = 0; i < EDID_STD_TIMINGS; i++) { 24535718399fSFrançois Tigeot struct drm_display_mode *newmode; 24545718399fSFrançois Tigeot 24555718399fSFrançois Tigeot newmode = drm_mode_std(connector, edid, 2456ba55f2f5SFrançois Tigeot &edid->standard_timings[i]); 24575718399fSFrançois Tigeot if (newmode) { 24585718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 24595718399fSFrançois Tigeot modes++; 24605718399fSFrançois Tigeot } 24615718399fSFrançois Tigeot } 24625718399fSFrançois Tigeot 24635718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 24645718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_standard_modes, 24655718399fSFrançois Tigeot &closure); 24665718399fSFrançois Tigeot 24675718399fSFrançois Tigeot /* XXX should also look for standard codes in VTB blocks */ 24685718399fSFrançois Tigeot 24695718399fSFrançois Tigeot return modes + closure.modes; 24705718399fSFrançois Tigeot } 24715718399fSFrançois Tigeot 24725718399fSFrançois Tigeot static int drm_cvt_modes(struct drm_connector *connector, 24735718399fSFrançois Tigeot struct detailed_timing *timing) 24745718399fSFrançois Tigeot { 24755718399fSFrançois Tigeot int i, j, modes = 0; 24765718399fSFrançois Tigeot struct drm_display_mode *newmode; 24775718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 24785718399fSFrançois Tigeot struct cvt_timing *cvt; 24795718399fSFrançois Tigeot const int rates[] = { 60, 85, 75, 60, 50 }; 24805718399fSFrançois Tigeot const u8 empty[3] = { 0, 0, 0 }; 24815718399fSFrançois Tigeot 24825718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 2483a3faafcbSSascha Wildner int width = 0, height; 24845718399fSFrançois Tigeot cvt = &(timing->data.other_data.data.cvt[i]); 24855718399fSFrançois Tigeot 24865718399fSFrançois Tigeot if (!memcmp(cvt->code, empty, 3)) 24875718399fSFrançois Tigeot continue; 24885718399fSFrançois Tigeot 24895718399fSFrançois Tigeot height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; 24905718399fSFrançois Tigeot switch (cvt->code[1] & 0x0c) { 24915718399fSFrançois Tigeot case 0x00: 24925718399fSFrançois Tigeot width = height * 4 / 3; 24935718399fSFrançois Tigeot break; 24945718399fSFrançois Tigeot case 0x04: 24955718399fSFrançois Tigeot width = height * 16 / 9; 24965718399fSFrançois Tigeot break; 24975718399fSFrançois Tigeot case 0x08: 24985718399fSFrançois Tigeot width = height * 16 / 10; 24995718399fSFrançois Tigeot break; 25005718399fSFrançois Tigeot case 0x0c: 25015718399fSFrançois Tigeot width = height * 15 / 9; 25025718399fSFrançois Tigeot break; 25035718399fSFrançois Tigeot } 25045718399fSFrançois Tigeot 25055718399fSFrançois Tigeot for (j = 1; j < 5; j++) { 25065718399fSFrançois Tigeot if (cvt->code[2] & (1 << j)) { 25075718399fSFrançois Tigeot newmode = drm_cvt_mode(dev, width, height, 25085718399fSFrançois Tigeot rates[j], j == 0, 25095718399fSFrançois Tigeot false, false); 25105718399fSFrançois Tigeot if (newmode) { 25115718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 25125718399fSFrançois Tigeot modes++; 25135718399fSFrançois Tigeot } 25145718399fSFrançois Tigeot } 25155718399fSFrançois Tigeot } 25165718399fSFrançois Tigeot } 25175718399fSFrançois Tigeot 25185718399fSFrançois Tigeot return modes; 25195718399fSFrançois Tigeot } 25205718399fSFrançois Tigeot 25215718399fSFrançois Tigeot static void 25225718399fSFrançois Tigeot do_cvt_mode(struct detailed_timing *timing, void *c) 25235718399fSFrançois Tigeot { 25245718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 25255718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 25265718399fSFrançois Tigeot 25275718399fSFrançois Tigeot if (data->type == EDID_DETAIL_CVT_3BYTE) 25285718399fSFrançois Tigeot closure->modes += drm_cvt_modes(closure->connector, timing); 25295718399fSFrançois Tigeot } 25305718399fSFrançois Tigeot 25315718399fSFrançois Tigeot static int 25325718399fSFrançois Tigeot add_cvt_modes(struct drm_connector *connector, struct edid *edid) 25335718399fSFrançois Tigeot { 25345718399fSFrançois Tigeot struct detailed_mode_closure closure = { 253555a9fa67Szrj .connector = connector, 253655a9fa67Szrj .edid = edid, 25375718399fSFrançois Tigeot }; 25385718399fSFrançois Tigeot 25395718399fSFrançois Tigeot if (version_greater(edid, 1, 2)) 25405718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); 25415718399fSFrançois Tigeot 25425718399fSFrançois Tigeot /* XXX should also look for CVT codes in VTB blocks */ 25435718399fSFrançois Tigeot 25445718399fSFrançois Tigeot return closure.modes; 25455718399fSFrançois Tigeot } 25465718399fSFrançois Tigeot 2547352ff8bdSFrançois Tigeot static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode); 2548352ff8bdSFrançois Tigeot 25495718399fSFrançois Tigeot static void 25505718399fSFrançois Tigeot do_detailed_mode(struct detailed_timing *timing, void *c) 25515718399fSFrançois Tigeot { 25525718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 25535718399fSFrançois Tigeot struct drm_display_mode *newmode; 25545718399fSFrançois Tigeot 25555718399fSFrançois Tigeot if (timing->pixel_clock) { 25565718399fSFrançois Tigeot newmode = drm_mode_detailed(closure->connector->dev, 25575718399fSFrançois Tigeot closure->edid, timing, 25585718399fSFrançois Tigeot closure->quirks); 25595718399fSFrançois Tigeot if (!newmode) 25605718399fSFrançois Tigeot return; 25615718399fSFrançois Tigeot 25625718399fSFrançois Tigeot if (closure->preferred) 25635718399fSFrançois Tigeot newmode->type |= DRM_MODE_TYPE_PREFERRED; 25645718399fSFrançois Tigeot 2565352ff8bdSFrançois Tigeot /* 2566352ff8bdSFrançois Tigeot * Detailed modes are limited to 10kHz pixel clock resolution, 2567352ff8bdSFrançois Tigeot * so fix up anything that looks like CEA/HDMI mode, but the clock 2568352ff8bdSFrançois Tigeot * is just slightly off. 2569352ff8bdSFrançois Tigeot */ 2570352ff8bdSFrançois Tigeot fixup_detailed_cea_mode_clock(newmode); 2571352ff8bdSFrançois Tigeot 25725718399fSFrançois Tigeot drm_mode_probed_add(closure->connector, newmode); 25735718399fSFrançois Tigeot closure->modes++; 25745718399fSFrançois Tigeot closure->preferred = 0; 25755718399fSFrançois Tigeot } 25765718399fSFrançois Tigeot } 25775718399fSFrançois Tigeot 25785718399fSFrançois Tigeot /* 25795718399fSFrançois Tigeot * add_detailed_modes - Add modes from detailed timings 25805718399fSFrançois Tigeot * @connector: attached connector 25815718399fSFrançois Tigeot * @edid: EDID block to scan 25825718399fSFrançois Tigeot * @quirks: quirks to apply 25835718399fSFrançois Tigeot */ 25845718399fSFrançois Tigeot static int 25855718399fSFrançois Tigeot add_detailed_modes(struct drm_connector *connector, struct edid *edid, 25865718399fSFrançois Tigeot u32 quirks) 25875718399fSFrançois Tigeot { 25885718399fSFrançois Tigeot struct detailed_mode_closure closure = { 258955a9fa67Szrj .connector = connector, 259055a9fa67Szrj .edid = edid, 259155a9fa67Szrj .preferred = 1, 259255a9fa67Szrj .quirks = quirks, 25935718399fSFrançois Tigeot }; 25945718399fSFrançois Tigeot 25955718399fSFrançois Tigeot if (closure.preferred && !version_greater(edid, 1, 3)) 25965718399fSFrançois Tigeot closure.preferred = 25975718399fSFrançois Tigeot (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); 25985718399fSFrançois Tigeot 25995718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); 26005718399fSFrançois Tigeot 26015718399fSFrançois Tigeot return closure.modes; 26025718399fSFrançois Tigeot } 26035718399fSFrançois Tigeot 26045718399fSFrançois Tigeot #define AUDIO_BLOCK 0x01 26056e29dde8SFrançois Tigeot #define VIDEO_BLOCK 0x02 26065718399fSFrançois Tigeot #define VENDOR_BLOCK 0x03 26075718399fSFrançois Tigeot #define SPEAKER_BLOCK 0x04 2608a2fdbec6SFrançois Tigeot #define VIDEO_CAPABILITY_BLOCK 0x07 26095718399fSFrançois Tigeot #define EDID_BASIC_AUDIO (1 << 6) 26106e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB444 (1 << 5) 26116e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB422 (1 << 4) 2612a2fdbec6SFrançois Tigeot #define EDID_CEA_VCDB_QS (1 << 6) 26135718399fSFrançois Tigeot 26149edbd4a0SFrançois Tigeot /* 26155718399fSFrançois Tigeot * Search EDID for CEA extension block. 26165718399fSFrançois Tigeot */ 26178621f407SFrançois Tigeot static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) 26185718399fSFrançois Tigeot { 26195718399fSFrançois Tigeot u8 *edid_ext = NULL; 26205718399fSFrançois Tigeot int i; 26215718399fSFrançois Tigeot 26225718399fSFrançois Tigeot /* No EDID or EDID extensions */ 26235718399fSFrançois Tigeot if (edid == NULL || edid->extensions == 0) 26245718399fSFrançois Tigeot return NULL; 26255718399fSFrançois Tigeot 26265718399fSFrançois Tigeot /* Find CEA extension */ 26275718399fSFrançois Tigeot for (i = 0; i < edid->extensions; i++) { 26285718399fSFrançois Tigeot edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); 26298621f407SFrançois Tigeot if (edid_ext[0] == ext_id) 26305718399fSFrançois Tigeot break; 26315718399fSFrançois Tigeot } 26325718399fSFrançois Tigeot 26335718399fSFrançois Tigeot if (i == edid->extensions) 26345718399fSFrançois Tigeot return NULL; 26355718399fSFrançois Tigeot 26365718399fSFrançois Tigeot return edid_ext; 26375718399fSFrançois Tigeot } 26389edbd4a0SFrançois Tigeot 26398621f407SFrançois Tigeot static u8 *drm_find_cea_extension(struct edid *edid) 26408621f407SFrançois Tigeot { 26418621f407SFrançois Tigeot return drm_find_edid_extension(edid, CEA_EXT); 26428621f407SFrançois Tigeot } 26438621f407SFrançois Tigeot 26448621f407SFrançois Tigeot static u8 *drm_find_displayid_extension(struct edid *edid) 26458621f407SFrançois Tigeot { 26468621f407SFrançois Tigeot return drm_find_edid_extension(edid, DISPLAYID_EXT); 26478621f407SFrançois Tigeot } 26488621f407SFrançois Tigeot 26499edbd4a0SFrançois Tigeot /* 26509edbd4a0SFrançois Tigeot * Calculate the alternate clock for the CEA mode 26519edbd4a0SFrançois Tigeot * (60Hz vs. 59.94Hz etc.) 26529edbd4a0SFrançois Tigeot */ 26539edbd4a0SFrançois Tigeot static unsigned int 26549edbd4a0SFrançois Tigeot cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) 26559edbd4a0SFrançois Tigeot { 26569edbd4a0SFrançois Tigeot unsigned int clock = cea_mode->clock; 26579edbd4a0SFrançois Tigeot 26589edbd4a0SFrançois Tigeot if (cea_mode->vrefresh % 6 != 0) 26599edbd4a0SFrançois Tigeot return clock; 26609edbd4a0SFrançois Tigeot 26619edbd4a0SFrançois Tigeot /* 26629edbd4a0SFrançois Tigeot * edid_cea_modes contains the 59.94Hz 26639edbd4a0SFrançois Tigeot * variant for 240 and 480 line modes, 26649edbd4a0SFrançois Tigeot * and the 60Hz variant otherwise. 26659edbd4a0SFrançois Tigeot */ 26669edbd4a0SFrançois Tigeot if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480) 2667352ff8bdSFrançois Tigeot clock = DIV_ROUND_CLOSEST(clock * 1001, 1000); 26689edbd4a0SFrançois Tigeot else 2669352ff8bdSFrançois Tigeot clock = DIV_ROUND_CLOSEST(clock * 1000, 1001); 26709edbd4a0SFrançois Tigeot 26719edbd4a0SFrançois Tigeot return clock; 26729edbd4a0SFrançois Tigeot } 26735718399fSFrançois Tigeot 2674aee94f86SFrançois Tigeot static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match, 2675aee94f86SFrançois Tigeot unsigned int clock_tolerance) 2676aee94f86SFrançois Tigeot { 2677aee94f86SFrançois Tigeot u8 vic; 2678aee94f86SFrançois Tigeot 2679aee94f86SFrançois Tigeot if (!to_match->clock) 2680aee94f86SFrançois Tigeot return 0; 2681aee94f86SFrançois Tigeot 2682aee94f86SFrançois Tigeot for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) { 2683aee94f86SFrançois Tigeot const struct drm_display_mode *cea_mode = &edid_cea_modes[vic]; 2684aee94f86SFrançois Tigeot unsigned int clock1, clock2; 2685aee94f86SFrançois Tigeot 2686aee94f86SFrançois Tigeot /* Check both 60Hz and 59.94Hz */ 2687aee94f86SFrançois Tigeot clock1 = cea_mode->clock; 2688aee94f86SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 2689aee94f86SFrançois Tigeot 2690aee94f86SFrançois Tigeot if (abs(to_match->clock - clock1) > clock_tolerance && 2691aee94f86SFrançois Tigeot abs(to_match->clock - clock2) > clock_tolerance) 2692aee94f86SFrançois Tigeot continue; 2693aee94f86SFrançois Tigeot 2694aee94f86SFrançois Tigeot if (drm_mode_equal_no_clocks(to_match, cea_mode)) 2695aee94f86SFrançois Tigeot return vic; 2696aee94f86SFrançois Tigeot } 2697aee94f86SFrançois Tigeot 2698aee94f86SFrançois Tigeot return 0; 2699aee94f86SFrançois Tigeot } 2700aee94f86SFrançois Tigeot 2701d82bf20eSFrançois Tigeot /** 2702d82bf20eSFrançois Tigeot * drm_match_cea_mode - look for a CEA mode matching given mode 2703d82bf20eSFrançois Tigeot * @to_match: display mode 2704d82bf20eSFrançois Tigeot * 2705ba55f2f5SFrançois Tigeot * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 2706d82bf20eSFrançois Tigeot * mode. 27076e29dde8SFrançois Tigeot */ 2708a2fdbec6SFrançois Tigeot u8 drm_match_cea_mode(const struct drm_display_mode *to_match) 27095718399fSFrançois Tigeot { 2710aee94f86SFrançois Tigeot u8 vic; 27116e29dde8SFrançois Tigeot 27129edbd4a0SFrançois Tigeot if (!to_match->clock) 27139edbd4a0SFrançois Tigeot return 0; 27146e29dde8SFrançois Tigeot 2715aee94f86SFrançois Tigeot for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) { 2716aee94f86SFrançois Tigeot const struct drm_display_mode *cea_mode = &edid_cea_modes[vic]; 27179edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 27189edbd4a0SFrançois Tigeot 27199edbd4a0SFrançois Tigeot /* Check both 60Hz and 59.94Hz */ 27209edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 27219edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 27229edbd4a0SFrançois Tigeot 27239edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 27249edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 27259edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode)) 2726aee94f86SFrançois Tigeot return vic; 27276e29dde8SFrançois Tigeot } 27286e29dde8SFrançois Tigeot return 0; 27296e29dde8SFrançois Tigeot } 27306e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_match_cea_mode); 27316e29dde8SFrançois Tigeot 2732aee94f86SFrançois Tigeot static bool drm_valid_cea_vic(u8 vic) 2733aee94f86SFrançois Tigeot { 2734aee94f86SFrançois Tigeot return vic > 0 && vic < ARRAY_SIZE(edid_cea_modes); 2735aee94f86SFrançois Tigeot } 2736aee94f86SFrançois Tigeot 2737ba55f2f5SFrançois Tigeot /** 2738ba55f2f5SFrançois Tigeot * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to 2739ba55f2f5SFrançois Tigeot * the input VIC from the CEA mode list 2740ba55f2f5SFrançois Tigeot * @video_code: ID given to each of the CEA modes 2741ba55f2f5SFrançois Tigeot * 2742ba55f2f5SFrançois Tigeot * Returns picture aspect ratio 2743ba55f2f5SFrançois Tigeot */ 2744ba55f2f5SFrançois Tigeot enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) 2745ba55f2f5SFrançois Tigeot { 2746aee94f86SFrançois Tigeot return edid_cea_modes[video_code].picture_aspect_ratio; 2747ba55f2f5SFrançois Tigeot } 2748ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_cea_aspect_ratio); 2749ba55f2f5SFrançois Tigeot 27509edbd4a0SFrançois Tigeot /* 27519edbd4a0SFrançois Tigeot * Calculate the alternate clock for HDMI modes (those from the HDMI vendor 27529edbd4a0SFrançois Tigeot * specific block). 27539edbd4a0SFrançois Tigeot * 27549edbd4a0SFrançois Tigeot * It's almost like cea_mode_alternate_clock(), we just need to add an 27559edbd4a0SFrançois Tigeot * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this 27569edbd4a0SFrançois Tigeot * one. 27579edbd4a0SFrançois Tigeot */ 27589edbd4a0SFrançois Tigeot static unsigned int 27599edbd4a0SFrançois Tigeot hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode) 27609edbd4a0SFrançois Tigeot { 27619edbd4a0SFrançois Tigeot if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160) 27629edbd4a0SFrançois Tigeot return hdmi_mode->clock; 27639edbd4a0SFrançois Tigeot 27649edbd4a0SFrançois Tigeot return cea_mode_alternate_clock(hdmi_mode); 27659edbd4a0SFrançois Tigeot } 27669edbd4a0SFrançois Tigeot 2767aee94f86SFrançois Tigeot static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match, 2768aee94f86SFrançois Tigeot unsigned int clock_tolerance) 2769aee94f86SFrançois Tigeot { 2770aee94f86SFrançois Tigeot u8 vic; 2771aee94f86SFrançois Tigeot 2772aee94f86SFrançois Tigeot if (!to_match->clock) 2773aee94f86SFrançois Tigeot return 0; 2774aee94f86SFrançois Tigeot 2775aee94f86SFrançois Tigeot for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) { 2776aee94f86SFrançois Tigeot const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic]; 2777aee94f86SFrançois Tigeot unsigned int clock1, clock2; 2778aee94f86SFrançois Tigeot 2779aee94f86SFrançois Tigeot /* Make sure to also match alternate clocks */ 2780aee94f86SFrançois Tigeot clock1 = hdmi_mode->clock; 2781aee94f86SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(hdmi_mode); 2782aee94f86SFrançois Tigeot 2783aee94f86SFrançois Tigeot if (abs(to_match->clock - clock1) > clock_tolerance && 2784aee94f86SFrançois Tigeot abs(to_match->clock - clock2) > clock_tolerance) 2785aee94f86SFrançois Tigeot continue; 2786aee94f86SFrançois Tigeot 2787aee94f86SFrançois Tigeot if (drm_mode_equal_no_clocks(to_match, hdmi_mode)) 2788aee94f86SFrançois Tigeot return vic; 2789aee94f86SFrançois Tigeot } 2790aee94f86SFrançois Tigeot 2791aee94f86SFrançois Tigeot return 0; 2792aee94f86SFrançois Tigeot } 2793aee94f86SFrançois Tigeot 27949edbd4a0SFrançois Tigeot /* 27959edbd4a0SFrançois Tigeot * drm_match_hdmi_mode - look for a HDMI mode matching given mode 27969edbd4a0SFrançois Tigeot * @to_match: display mode 27979edbd4a0SFrançois Tigeot * 27989edbd4a0SFrançois Tigeot * An HDMI mode is one defined in the HDMI vendor specific block. 27999edbd4a0SFrançois Tigeot * 28009edbd4a0SFrançois Tigeot * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one. 28019edbd4a0SFrançois Tigeot */ 28029edbd4a0SFrançois Tigeot static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match) 28039edbd4a0SFrançois Tigeot { 2804aee94f86SFrançois Tigeot u8 vic; 28059edbd4a0SFrançois Tigeot 28069edbd4a0SFrançois Tigeot if (!to_match->clock) 28079edbd4a0SFrançois Tigeot return 0; 28089edbd4a0SFrançois Tigeot 2809aee94f86SFrançois Tigeot for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) { 2810aee94f86SFrançois Tigeot const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic]; 28119edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 28129edbd4a0SFrançois Tigeot 28139edbd4a0SFrançois Tigeot /* Make sure to also match alternate clocks */ 28149edbd4a0SFrançois Tigeot clock1 = hdmi_mode->clock; 28159edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(hdmi_mode); 28169edbd4a0SFrançois Tigeot 28179edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 28189edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 28199edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode)) 2820aee94f86SFrançois Tigeot return vic; 28219edbd4a0SFrançois Tigeot } 28229edbd4a0SFrançois Tigeot return 0; 28239edbd4a0SFrançois Tigeot } 28246e29dde8SFrançois Tigeot 2825aee94f86SFrançois Tigeot static bool drm_valid_hdmi_vic(u8 vic) 2826aee94f86SFrançois Tigeot { 2827aee94f86SFrançois Tigeot return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes); 2828aee94f86SFrançois Tigeot } 2829aee94f86SFrançois Tigeot 28306e29dde8SFrançois Tigeot static int 28319edbd4a0SFrançois Tigeot add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) 28326e29dde8SFrançois Tigeot { 28336e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 28349edbd4a0SFrançois Tigeot struct drm_display_mode *mode, *tmp; 28359edbd4a0SFrançois Tigeot LINUX_LIST_HEAD(list); 28366e29dde8SFrançois Tigeot int modes = 0; 28376e29dde8SFrançois Tigeot 28389edbd4a0SFrançois Tigeot /* Don't add CEA modes if the CEA extension block is missing */ 28399edbd4a0SFrançois Tigeot if (!drm_find_cea_extension(edid)) 28409edbd4a0SFrançois Tigeot return 0; 28419edbd4a0SFrançois Tigeot 28429edbd4a0SFrançois Tigeot /* 28439edbd4a0SFrançois Tigeot * Go through all probed modes and create a new mode 28449edbd4a0SFrançois Tigeot * with the alternate clock for certain CEA modes. 28459edbd4a0SFrançois Tigeot */ 28469edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 28479edbd4a0SFrançois Tigeot const struct drm_display_mode *cea_mode = NULL; 28486e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 2849aee94f86SFrançois Tigeot u8 vic = drm_match_cea_mode(mode); 28509edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 28519edbd4a0SFrançois Tigeot 2852aee94f86SFrançois Tigeot if (drm_valid_cea_vic(vic)) { 2853aee94f86SFrançois Tigeot cea_mode = &edid_cea_modes[vic]; 28549edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 28559edbd4a0SFrançois Tigeot } else { 2856aee94f86SFrançois Tigeot vic = drm_match_hdmi_mode(mode); 2857aee94f86SFrançois Tigeot if (drm_valid_hdmi_vic(vic)) { 2858aee94f86SFrançois Tigeot cea_mode = &edid_4k_modes[vic]; 28599edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(cea_mode); 28609edbd4a0SFrançois Tigeot } 28619edbd4a0SFrançois Tigeot } 28629edbd4a0SFrançois Tigeot 28639edbd4a0SFrançois Tigeot if (!cea_mode) 28649edbd4a0SFrançois Tigeot continue; 28659edbd4a0SFrançois Tigeot 28669edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 28679edbd4a0SFrançois Tigeot 28689edbd4a0SFrançois Tigeot if (clock1 == clock2) 28699edbd4a0SFrançois Tigeot continue; 28709edbd4a0SFrançois Tigeot 28719edbd4a0SFrançois Tigeot if (mode->clock != clock1 && mode->clock != clock2) 28729edbd4a0SFrançois Tigeot continue; 28739edbd4a0SFrançois Tigeot 28749edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, cea_mode); 28759edbd4a0SFrançois Tigeot if (!newmode) 28769edbd4a0SFrançois Tigeot continue; 28779edbd4a0SFrançois Tigeot 28789edbd4a0SFrançois Tigeot /* Carry over the stereo flags */ 28799edbd4a0SFrançois Tigeot newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK; 28809edbd4a0SFrançois Tigeot 28819edbd4a0SFrançois Tigeot /* 28829edbd4a0SFrançois Tigeot * The current mode could be either variant. Make 28839edbd4a0SFrançois Tigeot * sure to pick the "other" clock for the new mode. 28849edbd4a0SFrançois Tigeot */ 28859edbd4a0SFrançois Tigeot if (mode->clock != clock1) 28869edbd4a0SFrançois Tigeot newmode->clock = clock1; 28879edbd4a0SFrançois Tigeot else 28889edbd4a0SFrançois Tigeot newmode->clock = clock2; 28899edbd4a0SFrançois Tigeot 28909edbd4a0SFrançois Tigeot list_add_tail(&newmode->head, &list); 28919edbd4a0SFrançois Tigeot } 28929edbd4a0SFrançois Tigeot 28939edbd4a0SFrançois Tigeot list_for_each_entry_safe(mode, tmp, &list, head) { 28949edbd4a0SFrançois Tigeot list_del(&mode->head); 28959edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 28969edbd4a0SFrançois Tigeot modes++; 28979edbd4a0SFrançois Tigeot } 28989edbd4a0SFrançois Tigeot 28999edbd4a0SFrançois Tigeot return modes; 29009edbd4a0SFrançois Tigeot } 29019edbd4a0SFrançois Tigeot 29029edbd4a0SFrançois Tigeot static struct drm_display_mode * 29039edbd4a0SFrançois Tigeot drm_display_mode_from_vic_index(struct drm_connector *connector, 29049edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, 29059edbd4a0SFrançois Tigeot u8 video_index) 29069edbd4a0SFrançois Tigeot { 29079edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 29089edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 2909aee94f86SFrançois Tigeot u8 vic; 29109edbd4a0SFrançois Tigeot 29119edbd4a0SFrançois Tigeot if (video_db == NULL || video_index >= video_len) 29129edbd4a0SFrançois Tigeot return NULL; 29139edbd4a0SFrançois Tigeot 29149edbd4a0SFrançois Tigeot /* CEA modes are numbered 1..127 */ 2915aee94f86SFrançois Tigeot vic = (video_db[video_index] & 127); 2916aee94f86SFrançois Tigeot if (!drm_valid_cea_vic(vic)) 29179edbd4a0SFrançois Tigeot return NULL; 29189edbd4a0SFrançois Tigeot 2919aee94f86SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]); 2920ba55f2f5SFrançois Tigeot if (!newmode) 2921ba55f2f5SFrançois Tigeot return NULL; 2922ba55f2f5SFrançois Tigeot 29239edbd4a0SFrançois Tigeot newmode->vrefresh = 0; 29249edbd4a0SFrançois Tigeot 29259edbd4a0SFrançois Tigeot return newmode; 29269edbd4a0SFrançois Tigeot } 29279edbd4a0SFrançois Tigeot 29289edbd4a0SFrançois Tigeot static int 29299edbd4a0SFrançois Tigeot do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) 29309edbd4a0SFrançois Tigeot { 29319edbd4a0SFrançois Tigeot int i, modes = 0; 29329edbd4a0SFrançois Tigeot 29339edbd4a0SFrançois Tigeot for (i = 0; i < len; i++) { 29349edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 29359edbd4a0SFrançois Tigeot mode = drm_display_mode_from_vic_index(connector, db, len, i); 29369edbd4a0SFrançois Tigeot if (mode) { 29379edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 29389edbd4a0SFrançois Tigeot modes++; 29399edbd4a0SFrançois Tigeot } 29409edbd4a0SFrançois Tigeot } 29419edbd4a0SFrançois Tigeot 29429edbd4a0SFrançois Tigeot return modes; 29439edbd4a0SFrançois Tigeot } 29449edbd4a0SFrançois Tigeot 29459edbd4a0SFrançois Tigeot struct stereo_mandatory_mode { 29469edbd4a0SFrançois Tigeot int width, height, vrefresh; 29479edbd4a0SFrançois Tigeot unsigned int flags; 29489edbd4a0SFrançois Tigeot }; 29499edbd4a0SFrançois Tigeot 29509edbd4a0SFrançois Tigeot static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { 29519edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 29529edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, 29539edbd4a0SFrançois Tigeot { 1920, 1080, 50, 29549edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 29559edbd4a0SFrançois Tigeot { 1920, 1080, 60, 29569edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 29579edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 29589edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, 29599edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 29609edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } 29619edbd4a0SFrançois Tigeot }; 29629edbd4a0SFrançois Tigeot 29639edbd4a0SFrançois Tigeot static bool 29649edbd4a0SFrançois Tigeot stereo_match_mandatory(const struct drm_display_mode *mode, 29659edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *stereo_mode) 29669edbd4a0SFrançois Tigeot { 29679edbd4a0SFrançois Tigeot unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; 29689edbd4a0SFrançois Tigeot 29699edbd4a0SFrançois Tigeot return mode->hdisplay == stereo_mode->width && 29709edbd4a0SFrançois Tigeot mode->vdisplay == stereo_mode->height && 29719edbd4a0SFrançois Tigeot interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && 29729edbd4a0SFrançois Tigeot drm_mode_vrefresh(mode) == stereo_mode->vrefresh; 29739edbd4a0SFrançois Tigeot } 29749edbd4a0SFrançois Tigeot 29759edbd4a0SFrançois Tigeot static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) 29769edbd4a0SFrançois Tigeot { 29779edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 29789edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 29799edbd4a0SFrançois Tigeot struct list_head stereo_modes; 29809edbd4a0SFrançois Tigeot int modes = 0, i; 29819edbd4a0SFrançois Tigeot 29829edbd4a0SFrançois Tigeot INIT_LIST_HEAD(&stereo_modes); 29839edbd4a0SFrançois Tigeot 29849edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 29859edbd4a0SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { 29869edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *mandatory; 29879edbd4a0SFrançois Tigeot struct drm_display_mode *new_mode; 29889edbd4a0SFrançois Tigeot 29899edbd4a0SFrançois Tigeot if (!stereo_match_mandatory(mode, 29909edbd4a0SFrançois Tigeot &stereo_mandatory_modes[i])) 29919edbd4a0SFrançois Tigeot continue; 29929edbd4a0SFrançois Tigeot 29939edbd4a0SFrançois Tigeot mandatory = &stereo_mandatory_modes[i]; 29949edbd4a0SFrançois Tigeot new_mode = drm_mode_duplicate(dev, mode); 29959edbd4a0SFrançois Tigeot if (!new_mode) 29969edbd4a0SFrançois Tigeot continue; 29979edbd4a0SFrançois Tigeot 29989edbd4a0SFrançois Tigeot new_mode->flags |= mandatory->flags; 29999edbd4a0SFrançois Tigeot list_add_tail(&new_mode->head, &stereo_modes); 30009edbd4a0SFrançois Tigeot modes++; 30019edbd4a0SFrançois Tigeot } 30029edbd4a0SFrançois Tigeot } 30039edbd4a0SFrançois Tigeot 30049edbd4a0SFrançois Tigeot list_splice_tail(&stereo_modes, &connector->probed_modes); 30059edbd4a0SFrançois Tigeot 30069edbd4a0SFrançois Tigeot return modes; 30079edbd4a0SFrançois Tigeot } 30089edbd4a0SFrançois Tigeot 30099edbd4a0SFrançois Tigeot static int add_hdmi_mode(struct drm_connector *connector, u8 vic) 30109edbd4a0SFrançois Tigeot { 30119edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 30129edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 30139edbd4a0SFrançois Tigeot 3014aee94f86SFrançois Tigeot if (!drm_valid_hdmi_vic(vic)) { 30159edbd4a0SFrançois Tigeot DRM_ERROR("Unknown HDMI VIC: %d\n", vic); 30169edbd4a0SFrançois Tigeot return 0; 30179edbd4a0SFrançois Tigeot } 30189edbd4a0SFrançois Tigeot 30199edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); 30209edbd4a0SFrançois Tigeot if (!newmode) 30219edbd4a0SFrançois Tigeot return 0; 30229edbd4a0SFrançois Tigeot 30239edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 30249edbd4a0SFrançois Tigeot 30259edbd4a0SFrançois Tigeot return 1; 30269edbd4a0SFrançois Tigeot } 30279edbd4a0SFrançois Tigeot 30289edbd4a0SFrançois Tigeot static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, 30299edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, u8 video_index) 30309edbd4a0SFrançois Tigeot { 30319edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 30329edbd4a0SFrançois Tigeot int modes = 0; 30339edbd4a0SFrançois Tigeot 30349edbd4a0SFrançois Tigeot if (structure & (1 << 0)) { 30359edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 30369edbd4a0SFrançois Tigeot video_len, 30379edbd4a0SFrançois Tigeot video_index); 30386e29dde8SFrançois Tigeot if (newmode) { 30399edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; 30406e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 30416e29dde8SFrançois Tigeot modes++; 30426e29dde8SFrançois Tigeot } 30436e29dde8SFrançois Tigeot } 30449edbd4a0SFrançois Tigeot if (structure & (1 << 6)) { 30459edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 30469edbd4a0SFrançois Tigeot video_len, 30479edbd4a0SFrançois Tigeot video_index); 30489edbd4a0SFrançois Tigeot if (newmode) { 30499edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 30509edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 30519edbd4a0SFrançois Tigeot modes++; 30529edbd4a0SFrançois Tigeot } 30539edbd4a0SFrançois Tigeot } 30549edbd4a0SFrançois Tigeot if (structure & (1 << 8)) { 30559edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 30569edbd4a0SFrançois Tigeot video_len, 30579edbd4a0SFrançois Tigeot video_index); 30589edbd4a0SFrançois Tigeot if (newmode) { 30599edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 30609edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 30619edbd4a0SFrançois Tigeot modes++; 30629edbd4a0SFrançois Tigeot } 30636e29dde8SFrançois Tigeot } 30646e29dde8SFrançois Tigeot 30656e29dde8SFrançois Tigeot return modes; 30666e29dde8SFrançois Tigeot } 30676e29dde8SFrançois Tigeot 30689edbd4a0SFrançois Tigeot /* 30699edbd4a0SFrançois Tigeot * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block 30709edbd4a0SFrançois Tigeot * @connector: connector corresponding to the HDMI sink 30719edbd4a0SFrançois Tigeot * @db: start of the CEA vendor specific block 30729edbd4a0SFrançois Tigeot * @len: length of the CEA block payload, ie. one can access up to db[len] 30739edbd4a0SFrançois Tigeot * 30749edbd4a0SFrançois Tigeot * Parses the HDMI VSDB looking for modes to add to @connector. This function 30759edbd4a0SFrançois Tigeot * also adds the stereo 3d modes when applicable. 30769edbd4a0SFrançois Tigeot */ 30779edbd4a0SFrançois Tigeot static int 30789edbd4a0SFrançois Tigeot do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, 30799edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len) 30809edbd4a0SFrançois Tigeot { 30819edbd4a0SFrançois Tigeot int modes = 0, offset = 0, i, multi_present = 0, multi_len; 30829edbd4a0SFrançois Tigeot u8 vic_len, hdmi_3d_len = 0; 30839edbd4a0SFrançois Tigeot u16 mask; 30849edbd4a0SFrançois Tigeot u16 structure_all; 30859edbd4a0SFrançois Tigeot 30869edbd4a0SFrançois Tigeot if (len < 8) 30879edbd4a0SFrançois Tigeot goto out; 30889edbd4a0SFrançois Tigeot 30899edbd4a0SFrançois Tigeot /* no HDMI_Video_Present */ 30909edbd4a0SFrançois Tigeot if (!(db[8] & (1 << 5))) 30919edbd4a0SFrançois Tigeot goto out; 30929edbd4a0SFrançois Tigeot 30939edbd4a0SFrançois Tigeot /* Latency_Fields_Present */ 30949edbd4a0SFrançois Tigeot if (db[8] & (1 << 7)) 30959edbd4a0SFrançois Tigeot offset += 2; 30969edbd4a0SFrançois Tigeot 30979edbd4a0SFrançois Tigeot /* I_Latency_Fields_Present */ 30989edbd4a0SFrançois Tigeot if (db[8] & (1 << 6)) 30999edbd4a0SFrançois Tigeot offset += 2; 31009edbd4a0SFrançois Tigeot 31019edbd4a0SFrançois Tigeot /* the declared length is not long enough for the 2 first bytes 31029edbd4a0SFrançois Tigeot * of additional video format capabilities */ 31039edbd4a0SFrançois Tigeot if (len < (8 + offset + 2)) 31049edbd4a0SFrançois Tigeot goto out; 31059edbd4a0SFrançois Tigeot 31069edbd4a0SFrançois Tigeot /* 3D_Present */ 31079edbd4a0SFrançois Tigeot offset++; 31089edbd4a0SFrançois Tigeot if (db[8 + offset] & (1 << 7)) { 31099edbd4a0SFrançois Tigeot modes += add_hdmi_mandatory_stereo_modes(connector); 31109edbd4a0SFrançois Tigeot 31119edbd4a0SFrançois Tigeot /* 3D_Multi_present */ 31129edbd4a0SFrançois Tigeot multi_present = (db[8 + offset] & 0x60) >> 5; 31139edbd4a0SFrançois Tigeot } 31149edbd4a0SFrançois Tigeot 31159edbd4a0SFrançois Tigeot offset++; 31169edbd4a0SFrançois Tigeot vic_len = db[8 + offset] >> 5; 31179edbd4a0SFrançois Tigeot hdmi_3d_len = db[8 + offset] & 0x1f; 31189edbd4a0SFrançois Tigeot 31199edbd4a0SFrançois Tigeot for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { 31209edbd4a0SFrançois Tigeot u8 vic; 31219edbd4a0SFrançois Tigeot 31229edbd4a0SFrançois Tigeot vic = db[9 + offset + i]; 31239edbd4a0SFrançois Tigeot modes += add_hdmi_mode(connector, vic); 31249edbd4a0SFrançois Tigeot } 31259edbd4a0SFrançois Tigeot offset += 1 + vic_len; 31269edbd4a0SFrançois Tigeot 31279edbd4a0SFrançois Tigeot if (multi_present == 1) 31289edbd4a0SFrançois Tigeot multi_len = 2; 31299edbd4a0SFrançois Tigeot else if (multi_present == 2) 31309edbd4a0SFrançois Tigeot multi_len = 4; 31319edbd4a0SFrançois Tigeot else 31329edbd4a0SFrançois Tigeot multi_len = 0; 31339edbd4a0SFrançois Tigeot 31349edbd4a0SFrançois Tigeot if (len < (8 + offset + hdmi_3d_len - 1)) 31359edbd4a0SFrançois Tigeot goto out; 31369edbd4a0SFrançois Tigeot 31379edbd4a0SFrançois Tigeot if (hdmi_3d_len < multi_len) 31389edbd4a0SFrançois Tigeot goto out; 31399edbd4a0SFrançois Tigeot 31409edbd4a0SFrançois Tigeot if (multi_present == 1 || multi_present == 2) { 31419edbd4a0SFrançois Tigeot /* 3D_Structure_ALL */ 31429edbd4a0SFrançois Tigeot structure_all = (db[8 + offset] << 8) | db[9 + offset]; 31439edbd4a0SFrançois Tigeot 31449edbd4a0SFrançois Tigeot /* check if 3D_MASK is present */ 31459edbd4a0SFrançois Tigeot if (multi_present == 2) 31469edbd4a0SFrançois Tigeot mask = (db[10 + offset] << 8) | db[11 + offset]; 31479edbd4a0SFrançois Tigeot else 31489edbd4a0SFrançois Tigeot mask = 0xffff; 31499edbd4a0SFrançois Tigeot 31509edbd4a0SFrançois Tigeot for (i = 0; i < 16; i++) { 31519edbd4a0SFrançois Tigeot if (mask & (1 << i)) 31529edbd4a0SFrançois Tigeot modes += add_3d_struct_modes(connector, 31539edbd4a0SFrançois Tigeot structure_all, 31549edbd4a0SFrançois Tigeot video_db, 31559edbd4a0SFrançois Tigeot video_len, i); 31569edbd4a0SFrançois Tigeot } 31579edbd4a0SFrançois Tigeot } 31589edbd4a0SFrançois Tigeot 31599edbd4a0SFrançois Tigeot offset += multi_len; 31609edbd4a0SFrançois Tigeot 31619edbd4a0SFrançois Tigeot for (i = 0; i < (hdmi_3d_len - multi_len); i++) { 31629edbd4a0SFrançois Tigeot int vic_index; 31639edbd4a0SFrançois Tigeot struct drm_display_mode *newmode = NULL; 31649edbd4a0SFrançois Tigeot unsigned int newflag = 0; 31659edbd4a0SFrançois Tigeot bool detail_present; 31669edbd4a0SFrançois Tigeot 31679edbd4a0SFrançois Tigeot detail_present = ((db[8 + offset + i] & 0x0f) > 7); 31689edbd4a0SFrançois Tigeot 31699edbd4a0SFrançois Tigeot if (detail_present && (i + 1 == hdmi_3d_len - multi_len)) 31709edbd4a0SFrançois Tigeot break; 31719edbd4a0SFrançois Tigeot 31729edbd4a0SFrançois Tigeot /* 2D_VIC_order_X */ 31739edbd4a0SFrançois Tigeot vic_index = db[8 + offset + i] >> 4; 31749edbd4a0SFrançois Tigeot 31759edbd4a0SFrançois Tigeot /* 3D_Structure_X */ 31769edbd4a0SFrançois Tigeot switch (db[8 + offset + i] & 0x0f) { 31779edbd4a0SFrançois Tigeot case 0: 31789edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_FRAME_PACKING; 31799edbd4a0SFrançois Tigeot break; 31809edbd4a0SFrançois Tigeot case 6: 31819edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 31829edbd4a0SFrançois Tigeot break; 31839edbd4a0SFrançois Tigeot case 8: 31849edbd4a0SFrançois Tigeot /* 3D_Detail_X */ 31859edbd4a0SFrançois Tigeot if ((db[9 + offset + i] >> 4) == 1) 31869edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 31879edbd4a0SFrançois Tigeot break; 31889edbd4a0SFrançois Tigeot } 31899edbd4a0SFrançois Tigeot 31909edbd4a0SFrançois Tigeot if (newflag != 0) { 31919edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, 31929edbd4a0SFrançois Tigeot video_db, 31939edbd4a0SFrançois Tigeot video_len, 31949edbd4a0SFrançois Tigeot vic_index); 31959edbd4a0SFrançois Tigeot 31969edbd4a0SFrançois Tigeot if (newmode) { 31979edbd4a0SFrançois Tigeot newmode->flags |= newflag; 31989edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 31999edbd4a0SFrançois Tigeot modes++; 32009edbd4a0SFrançois Tigeot } 32019edbd4a0SFrançois Tigeot } 32029edbd4a0SFrançois Tigeot 32039edbd4a0SFrançois Tigeot if (detail_present) 32049edbd4a0SFrançois Tigeot i++; 32059edbd4a0SFrançois Tigeot } 32069edbd4a0SFrançois Tigeot 32079edbd4a0SFrançois Tigeot out: 32089edbd4a0SFrançois Tigeot return modes; 32099edbd4a0SFrançois Tigeot } 32109edbd4a0SFrançois Tigeot 32116e29dde8SFrançois Tigeot static int 32126e29dde8SFrançois Tigeot cea_db_payload_len(const u8 *db) 32136e29dde8SFrançois Tigeot { 32146e29dde8SFrançois Tigeot return db[0] & 0x1f; 32156e29dde8SFrançois Tigeot } 32166e29dde8SFrançois Tigeot 32176e29dde8SFrançois Tigeot static int 32186e29dde8SFrançois Tigeot cea_db_tag(const u8 *db) 32196e29dde8SFrançois Tigeot { 32206e29dde8SFrançois Tigeot return db[0] >> 5; 32216e29dde8SFrançois Tigeot } 32226e29dde8SFrançois Tigeot 32236e29dde8SFrançois Tigeot static int 32246e29dde8SFrançois Tigeot cea_revision(const u8 *cea) 32256e29dde8SFrançois Tigeot { 32266e29dde8SFrançois Tigeot return cea[1]; 32276e29dde8SFrançois Tigeot } 32286e29dde8SFrançois Tigeot 32296e29dde8SFrançois Tigeot static int 32306e29dde8SFrançois Tigeot cea_db_offsets(const u8 *cea, int *start, int *end) 32316e29dde8SFrançois Tigeot { 32326e29dde8SFrançois Tigeot /* Data block offset in CEA extension block */ 32336e29dde8SFrançois Tigeot *start = 4; 32346e29dde8SFrançois Tigeot *end = cea[2]; 32356e29dde8SFrançois Tigeot if (*end == 0) 32366e29dde8SFrançois Tigeot *end = 127; 32376e29dde8SFrançois Tigeot if (*end < 4 || *end > 127) 32386e29dde8SFrançois Tigeot return -ERANGE; 32396e29dde8SFrançois Tigeot return 0; 32406e29dde8SFrançois Tigeot } 32416e29dde8SFrançois Tigeot 32429edbd4a0SFrançois Tigeot static bool cea_db_is_hdmi_vsdb(const u8 *db) 32439edbd4a0SFrançois Tigeot { 32449edbd4a0SFrançois Tigeot int hdmi_id; 32459edbd4a0SFrançois Tigeot 32469edbd4a0SFrançois Tigeot if (cea_db_tag(db) != VENDOR_BLOCK) 32479edbd4a0SFrançois Tigeot return false; 32489edbd4a0SFrançois Tigeot 32499edbd4a0SFrançois Tigeot if (cea_db_payload_len(db) < 5) 32509edbd4a0SFrançois Tigeot return false; 32519edbd4a0SFrançois Tigeot 32529edbd4a0SFrançois Tigeot hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); 32539edbd4a0SFrançois Tigeot 32549edbd4a0SFrançois Tigeot return hdmi_id == HDMI_IEEE_OUI; 32559edbd4a0SFrançois Tigeot } 32569edbd4a0SFrançois Tigeot 32576e29dde8SFrançois Tigeot #define for_each_cea_db(cea, i, start, end) \ 32586e29dde8SFrançois Tigeot for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) 32596e29dde8SFrançois Tigeot 32606e29dde8SFrançois Tigeot static int 32616e29dde8SFrançois Tigeot add_cea_modes(struct drm_connector *connector, struct edid *edid) 32626e29dde8SFrançois Tigeot { 32639edbd4a0SFrançois Tigeot const u8 *cea = drm_find_cea_extension(edid); 32649edbd4a0SFrançois Tigeot const u8 *db, *hdmi = NULL, *video = NULL; 32659edbd4a0SFrançois Tigeot u8 dbl, hdmi_len, video_len = 0; 32666e29dde8SFrançois Tigeot int modes = 0; 32676e29dde8SFrançois Tigeot 32686e29dde8SFrançois Tigeot if (cea && cea_revision(cea) >= 3) { 32696e29dde8SFrançois Tigeot int i, start, end; 32706e29dde8SFrançois Tigeot 32716e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) 32726e29dde8SFrançois Tigeot return 0; 32736e29dde8SFrançois Tigeot 32746e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 32756e29dde8SFrançois Tigeot db = &cea[i]; 32766e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 32776e29dde8SFrançois Tigeot 32789edbd4a0SFrançois Tigeot if (cea_db_tag(db) == VIDEO_BLOCK) { 32799edbd4a0SFrançois Tigeot video = db + 1; 32809edbd4a0SFrançois Tigeot video_len = dbl; 32819edbd4a0SFrançois Tigeot modes += do_cea_modes(connector, video, dbl); 32829edbd4a0SFrançois Tigeot } 32839edbd4a0SFrançois Tigeot else if (cea_db_is_hdmi_vsdb(db)) { 32849edbd4a0SFrançois Tigeot hdmi = db; 32859edbd4a0SFrançois Tigeot hdmi_len = dbl; 32866e29dde8SFrançois Tigeot } 32876e29dde8SFrançois Tigeot } 32889edbd4a0SFrançois Tigeot } 32899edbd4a0SFrançois Tigeot 32909edbd4a0SFrançois Tigeot /* 32919edbd4a0SFrançois Tigeot * We parse the HDMI VSDB after having added the cea modes as we will 32929edbd4a0SFrançois Tigeot * be patching their flags when the sink supports stereo 3D. 32939edbd4a0SFrançois Tigeot */ 32949edbd4a0SFrançois Tigeot if (hdmi) 32959edbd4a0SFrançois Tigeot modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, 32969edbd4a0SFrançois Tigeot video_len); 32976e29dde8SFrançois Tigeot 32986e29dde8SFrançois Tigeot return modes; 32996e29dde8SFrançois Tigeot } 33006e29dde8SFrançois Tigeot 3301352ff8bdSFrançois Tigeot static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) 3302352ff8bdSFrançois Tigeot { 3303352ff8bdSFrançois Tigeot const struct drm_display_mode *cea_mode; 3304352ff8bdSFrançois Tigeot int clock1, clock2, clock; 3305aee94f86SFrançois Tigeot u8 vic; 3306352ff8bdSFrançois Tigeot const char *type; 3307352ff8bdSFrançois Tigeot 3308aee94f86SFrançois Tigeot /* 3309aee94f86SFrançois Tigeot * allow 5kHz clock difference either way to account for 3310aee94f86SFrançois Tigeot * the 10kHz clock resolution limit of detailed timings. 3311aee94f86SFrançois Tigeot */ 3312aee94f86SFrançois Tigeot vic = drm_match_cea_mode_clock_tolerance(mode, 5); 3313aee94f86SFrançois Tigeot if (drm_valid_cea_vic(vic)) { 3314352ff8bdSFrançois Tigeot type = "CEA"; 3315aee94f86SFrançois Tigeot cea_mode = &edid_cea_modes[vic]; 3316352ff8bdSFrançois Tigeot clock1 = cea_mode->clock; 3317352ff8bdSFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 3318352ff8bdSFrançois Tigeot } else { 3319aee94f86SFrançois Tigeot vic = drm_match_hdmi_mode_clock_tolerance(mode, 5); 3320aee94f86SFrançois Tigeot if (drm_valid_hdmi_vic(vic)) { 3321352ff8bdSFrançois Tigeot type = "HDMI"; 3322aee94f86SFrançois Tigeot cea_mode = &edid_4k_modes[vic]; 3323352ff8bdSFrançois Tigeot clock1 = cea_mode->clock; 3324352ff8bdSFrançois Tigeot clock2 = hdmi_mode_alternate_clock(cea_mode); 3325352ff8bdSFrançois Tigeot } else { 3326352ff8bdSFrançois Tigeot return; 3327352ff8bdSFrançois Tigeot } 3328352ff8bdSFrançois Tigeot } 3329352ff8bdSFrançois Tigeot 3330352ff8bdSFrançois Tigeot /* pick whichever is closest */ 3331352ff8bdSFrançois Tigeot if (abs(mode->clock - clock1) < abs(mode->clock - clock2)) 3332352ff8bdSFrançois Tigeot clock = clock1; 3333352ff8bdSFrançois Tigeot else 3334352ff8bdSFrançois Tigeot clock = clock2; 3335352ff8bdSFrançois Tigeot 3336352ff8bdSFrançois Tigeot if (mode->clock == clock) 3337352ff8bdSFrançois Tigeot return; 3338352ff8bdSFrançois Tigeot 3339352ff8bdSFrançois Tigeot DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n", 3340aee94f86SFrançois Tigeot type, vic, mode->clock, clock); 3341352ff8bdSFrançois Tigeot mode->clock = clock; 3342352ff8bdSFrançois Tigeot } 3343352ff8bdSFrançois Tigeot 33446e29dde8SFrançois Tigeot static void 33456e29dde8SFrançois Tigeot parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) 33466e29dde8SFrançois Tigeot { 33476e29dde8SFrançois Tigeot u8 len = cea_db_payload_len(db); 33486e29dde8SFrançois Tigeot 33496e29dde8SFrançois Tigeot if (len >= 6) { 33505718399fSFrançois Tigeot connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ 33515718399fSFrançois Tigeot connector->dvi_dual = db[6] & 1; 33526e29dde8SFrançois Tigeot } 33536e29dde8SFrançois Tigeot if (len >= 7) 33545718399fSFrançois Tigeot connector->max_tmds_clock = db[7] * 5; 33556e29dde8SFrançois Tigeot if (len >= 8) { 33565718399fSFrançois Tigeot connector->latency_present[0] = db[8] >> 7; 33575718399fSFrançois Tigeot connector->latency_present[1] = (db[8] >> 6) & 1; 33586e29dde8SFrançois Tigeot } 33596e29dde8SFrançois Tigeot if (len >= 9) 33605718399fSFrançois Tigeot connector->video_latency[0] = db[9]; 33616e29dde8SFrançois Tigeot if (len >= 10) 33625718399fSFrançois Tigeot connector->audio_latency[0] = db[10]; 33636e29dde8SFrançois Tigeot if (len >= 11) 33645718399fSFrançois Tigeot connector->video_latency[1] = db[11]; 33656e29dde8SFrançois Tigeot if (len >= 12) 33665718399fSFrançois Tigeot connector->audio_latency[1] = db[12]; 33675718399fSFrançois Tigeot 33685718399fSFrançois Tigeot DRM_DEBUG_KMS("HDMI: DVI dual %d, " 33695718399fSFrançois Tigeot "max TMDS clock %d, " 33705718399fSFrançois Tigeot "latency present %d %d, " 33715718399fSFrançois Tigeot "video latency %d %d, " 33725718399fSFrançois Tigeot "audio latency %d %d\n", 33735718399fSFrançois Tigeot connector->dvi_dual, 33745718399fSFrançois Tigeot connector->max_tmds_clock, 33755718399fSFrançois Tigeot (int) connector->latency_present[0], 33765718399fSFrançois Tigeot (int) connector->latency_present[1], 33775718399fSFrançois Tigeot connector->video_latency[0], 33785718399fSFrançois Tigeot connector->video_latency[1], 33795718399fSFrançois Tigeot connector->audio_latency[0], 33805718399fSFrançois Tigeot connector->audio_latency[1]); 33815718399fSFrançois Tigeot } 33825718399fSFrançois Tigeot 33835718399fSFrançois Tigeot static void 33845718399fSFrançois Tigeot monitor_name(struct detailed_timing *t, void *data) 33855718399fSFrançois Tigeot { 33865718399fSFrançois Tigeot if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME) 33875718399fSFrançois Tigeot *(u8 **)data = t->data.other_data.data.str.str; 33885718399fSFrançois Tigeot } 33895718399fSFrançois Tigeot 33908621f407SFrançois Tigeot static int get_monitor_name(struct edid *edid, char name[13]) 33918621f407SFrançois Tigeot { 33928621f407SFrançois Tigeot char *edid_name = NULL; 33938621f407SFrançois Tigeot int mnl; 33948621f407SFrançois Tigeot 33958621f407SFrançois Tigeot if (!edid || !name) 33968621f407SFrançois Tigeot return 0; 33978621f407SFrançois Tigeot 33988621f407SFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, monitor_name, &edid_name); 33998621f407SFrançois Tigeot for (mnl = 0; edid_name && mnl < 13; mnl++) { 34008621f407SFrançois Tigeot if (edid_name[mnl] == 0x0a) 34018621f407SFrançois Tigeot break; 34028621f407SFrançois Tigeot 34038621f407SFrançois Tigeot name[mnl] = edid_name[mnl]; 34048621f407SFrançois Tigeot } 34058621f407SFrançois Tigeot 34068621f407SFrançois Tigeot return mnl; 34078621f407SFrançois Tigeot } 34088621f407SFrançois Tigeot 34098621f407SFrançois Tigeot /** 34108621f407SFrançois Tigeot * drm_edid_get_monitor_name - fetch the monitor name from the edid 34118621f407SFrançois Tigeot * @edid: monitor EDID information 34128621f407SFrançois Tigeot * @name: pointer to a character array to hold the name of the monitor 34138621f407SFrançois Tigeot * @bufsize: The size of the name buffer (should be at least 14 chars.) 34148621f407SFrançois Tigeot * 34158621f407SFrançois Tigeot */ 34168621f407SFrançois Tigeot void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize) 34178621f407SFrançois Tigeot { 34188621f407SFrançois Tigeot int name_length; 34198621f407SFrançois Tigeot char buf[13]; 34208621f407SFrançois Tigeot 34218621f407SFrançois Tigeot if (bufsize <= 0) 34228621f407SFrançois Tigeot return; 34238621f407SFrançois Tigeot 34248621f407SFrançois Tigeot name_length = min(get_monitor_name(edid, buf), bufsize - 1); 34258621f407SFrançois Tigeot memcpy(name, buf, name_length); 34268621f407SFrançois Tigeot name[name_length] = '\0'; 34278621f407SFrançois Tigeot } 34288621f407SFrançois Tigeot EXPORT_SYMBOL(drm_edid_get_monitor_name); 34298621f407SFrançois Tigeot 34305718399fSFrançois Tigeot /** 34315718399fSFrançois Tigeot * drm_edid_to_eld - build ELD from EDID 34325718399fSFrançois Tigeot * @connector: connector corresponding to the HDMI/DP sink 34335718399fSFrançois Tigeot * @edid: EDID to parse 34345718399fSFrançois Tigeot * 3435ba55f2f5SFrançois Tigeot * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The 3436ba55f2f5SFrançois Tigeot * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to 3437ba55f2f5SFrançois Tigeot * fill in. 34385718399fSFrançois Tigeot */ 34395718399fSFrançois Tigeot void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) 34405718399fSFrançois Tigeot { 34415718399fSFrançois Tigeot uint8_t *eld = connector->eld; 34425718399fSFrançois Tigeot u8 *cea; 34435718399fSFrançois Tigeot u8 *db; 3444c0e85e96SFrançois Tigeot int total_sad_count = 0; 34455718399fSFrançois Tigeot int mnl; 34465718399fSFrançois Tigeot int dbl; 34475718399fSFrançois Tigeot 34485718399fSFrançois Tigeot memset(eld, 0, sizeof(connector->eld)); 34495718399fSFrançois Tigeot 34505718399fSFrançois Tigeot cea = drm_find_cea_extension(edid); 34515718399fSFrançois Tigeot if (!cea) { 34525718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD: no CEA Extension found\n"); 34535718399fSFrançois Tigeot return; 34545718399fSFrançois Tigeot } 34555718399fSFrançois Tigeot 34568621f407SFrançois Tigeot mnl = get_monitor_name(edid, eld + 20); 34578621f407SFrançois Tigeot 34585718399fSFrançois Tigeot eld[4] = (cea[1] << 5) | mnl; 34595718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20); 34605718399fSFrançois Tigeot 34615718399fSFrançois Tigeot eld[0] = 2 << 3; /* ELD version: 2 */ 34625718399fSFrançois Tigeot 34635718399fSFrançois Tigeot eld[16] = edid->mfg_id[0]; 34645718399fSFrançois Tigeot eld[17] = edid->mfg_id[1]; 34655718399fSFrançois Tigeot eld[18] = edid->prod_code[0]; 34665718399fSFrançois Tigeot eld[19] = edid->prod_code[1]; 34675718399fSFrançois Tigeot 34686e29dde8SFrançois Tigeot if (cea_revision(cea) >= 3) { 34696e29dde8SFrançois Tigeot int i, start, end; 34705718399fSFrançois Tigeot 34716e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 34726e29dde8SFrançois Tigeot start = 0; 34736e29dde8SFrançois Tigeot end = 0; 34746e29dde8SFrançois Tigeot } 34756e29dde8SFrançois Tigeot 34766e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 34776e29dde8SFrançois Tigeot db = &cea[i]; 34786e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 34796e29dde8SFrançois Tigeot 34806e29dde8SFrançois Tigeot switch (cea_db_tag(db)) { 3481c0e85e96SFrançois Tigeot int sad_count; 3482c0e85e96SFrançois Tigeot 34836e29dde8SFrançois Tigeot case AUDIO_BLOCK: 34846e29dde8SFrançois Tigeot /* Audio Data Block, contains SADs */ 3485c0e85e96SFrançois Tigeot sad_count = min(dbl / 3, 15 - total_sad_count); 3486c0e85e96SFrançois Tigeot if (sad_count >= 1) 3487c0e85e96SFrançois Tigeot memcpy(eld + 20 + mnl + total_sad_count * 3, 3488c0e85e96SFrançois Tigeot &db[1], sad_count * 3); 3489c0e85e96SFrançois Tigeot total_sad_count += sad_count; 34905718399fSFrançois Tigeot break; 34916e29dde8SFrançois Tigeot case SPEAKER_BLOCK: 34926e29dde8SFrançois Tigeot /* Speaker Allocation Data Block */ 34936e29dde8SFrançois Tigeot if (dbl >= 1) 34945718399fSFrançois Tigeot eld[7] = db[1]; 34955718399fSFrançois Tigeot break; 34965718399fSFrançois Tigeot case VENDOR_BLOCK: 34975718399fSFrançois Tigeot /* HDMI Vendor-Specific Data Block */ 34986e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(db)) 34995718399fSFrançois Tigeot parse_hdmi_vsdb(connector, db); 35005718399fSFrançois Tigeot break; 35015718399fSFrançois Tigeot default: 35025718399fSFrançois Tigeot break; 35035718399fSFrançois Tigeot } 35045718399fSFrançois Tigeot } 35056e29dde8SFrançois Tigeot } 3506c0e85e96SFrançois Tigeot eld[5] |= total_sad_count << 4; 35075718399fSFrançois Tigeot 3508c0e85e96SFrançois Tigeot eld[DRM_ELD_BASELINE_ELD_LEN] = 3509c0e85e96SFrançois Tigeot DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); 3510c0e85e96SFrançois Tigeot 3511c0e85e96SFrançois Tigeot DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", 3512c0e85e96SFrançois Tigeot drm_eld_size(eld), total_sad_count); 35135718399fSFrançois Tigeot } 35146e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_eld); 35155718399fSFrançois Tigeot 35165718399fSFrançois Tigeot /** 35179edbd4a0SFrançois Tigeot * drm_edid_to_sad - extracts SADs from EDID 35189edbd4a0SFrançois Tigeot * @edid: EDID to parse 35199edbd4a0SFrançois Tigeot * @sads: pointer that will be set to the extracted SADs 35209edbd4a0SFrançois Tigeot * 35219edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. 35229edbd4a0SFrançois Tigeot * 3523ba55f2f5SFrançois Tigeot * Note: The returned pointer needs to be freed using kfree(). 3524ba55f2f5SFrançois Tigeot * 3525ba55f2f5SFrançois Tigeot * Return: The number of found SADs or negative number on error. 35269edbd4a0SFrançois Tigeot */ 35279edbd4a0SFrançois Tigeot int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) 35289edbd4a0SFrançois Tigeot { 35299edbd4a0SFrançois Tigeot int count = 0; 35309edbd4a0SFrançois Tigeot int i, start, end, dbl; 35319edbd4a0SFrançois Tigeot u8 *cea; 35329edbd4a0SFrançois Tigeot 35339edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 35349edbd4a0SFrançois Tigeot if (!cea) { 35359edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 35369edbd4a0SFrançois Tigeot return -ENOENT; 35379edbd4a0SFrançois Tigeot } 35389edbd4a0SFrançois Tigeot 35399edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 35409edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 3541f43cf1b1SMichael Neumann return -EOPNOTSUPP; 35429edbd4a0SFrançois Tigeot } 35439edbd4a0SFrançois Tigeot 35449edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 35459edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 35469edbd4a0SFrançois Tigeot return -EPROTO; 35479edbd4a0SFrançois Tigeot } 35489edbd4a0SFrançois Tigeot 35499edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 35509edbd4a0SFrançois Tigeot u8 *db = &cea[i]; 35519edbd4a0SFrançois Tigeot 35529edbd4a0SFrançois Tigeot if (cea_db_tag(db) == AUDIO_BLOCK) { 35539edbd4a0SFrançois Tigeot int j; 35549edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 35559edbd4a0SFrançois Tigeot 35569edbd4a0SFrançois Tigeot count = dbl / 3; /* SAD is 3B */ 355722ee5efbSFrançois Tigeot *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); 35589edbd4a0SFrançois Tigeot if (!*sads) 35599edbd4a0SFrançois Tigeot return -ENOMEM; 35609edbd4a0SFrançois Tigeot for (j = 0; j < count; j++) { 35619edbd4a0SFrançois Tigeot u8 *sad = &db[1 + j * 3]; 35629edbd4a0SFrançois Tigeot 35639edbd4a0SFrançois Tigeot (*sads)[j].format = (sad[0] & 0x78) >> 3; 35649edbd4a0SFrançois Tigeot (*sads)[j].channels = sad[0] & 0x7; 35659edbd4a0SFrançois Tigeot (*sads)[j].freq = sad[1] & 0x7F; 35669edbd4a0SFrançois Tigeot (*sads)[j].byte2 = sad[2]; 35679edbd4a0SFrançois Tigeot } 35689edbd4a0SFrançois Tigeot break; 35699edbd4a0SFrançois Tigeot } 35709edbd4a0SFrançois Tigeot } 35719edbd4a0SFrançois Tigeot 35729edbd4a0SFrançois Tigeot return count; 35739edbd4a0SFrançois Tigeot } 35749edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_sad); 35759edbd4a0SFrançois Tigeot 35769edbd4a0SFrançois Tigeot /** 35779edbd4a0SFrançois Tigeot * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID 35789edbd4a0SFrançois Tigeot * @edid: EDID to parse 35799edbd4a0SFrançois Tigeot * @sadb: pointer to the speaker block 35809edbd4a0SFrançois Tigeot * 35819edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. 35829edbd4a0SFrançois Tigeot * 3583ba55f2f5SFrançois Tigeot * Note: The returned pointer needs to be freed using kfree(). 3584ba55f2f5SFrançois Tigeot * 3585ba55f2f5SFrançois Tigeot * Return: The number of found Speaker Allocation Blocks or negative number on 3586ba55f2f5SFrançois Tigeot * error. 35879edbd4a0SFrançois Tigeot */ 35889edbd4a0SFrançois Tigeot int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) 35899edbd4a0SFrançois Tigeot { 35909edbd4a0SFrançois Tigeot int count = 0; 35919edbd4a0SFrançois Tigeot int i, start, end, dbl; 35929edbd4a0SFrançois Tigeot const u8 *cea; 35939edbd4a0SFrançois Tigeot 35949edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 35959edbd4a0SFrançois Tigeot if (!cea) { 35969edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 35979edbd4a0SFrançois Tigeot return -ENOENT; 35989edbd4a0SFrançois Tigeot } 35999edbd4a0SFrançois Tigeot 36009edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 36019edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 36029edbd4a0SFrançois Tigeot return -ENOTSUPP; 36039edbd4a0SFrançois Tigeot } 36049edbd4a0SFrançois Tigeot 36059edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 36069edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 36079edbd4a0SFrançois Tigeot return -EPROTO; 36089edbd4a0SFrançois Tigeot } 36099edbd4a0SFrançois Tigeot 36109edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 36119edbd4a0SFrançois Tigeot const u8 *db = &cea[i]; 36129edbd4a0SFrançois Tigeot 36139edbd4a0SFrançois Tigeot if (cea_db_tag(db) == SPEAKER_BLOCK) { 36149edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 36159edbd4a0SFrançois Tigeot 36169edbd4a0SFrançois Tigeot /* Speaker Allocation Data Block */ 36179edbd4a0SFrançois Tigeot if (dbl == 3) { 3618ba55f2f5SFrançois Tigeot *sadb = kmemdup(&db[1], dbl, GFP_KERNEL); 3619ba55f2f5SFrançois Tigeot if (!*sadb) 3620ba55f2f5SFrançois Tigeot return -ENOMEM; 36219edbd4a0SFrançois Tigeot count = dbl; 36229edbd4a0SFrançois Tigeot break; 36239edbd4a0SFrançois Tigeot } 36249edbd4a0SFrançois Tigeot } 36259edbd4a0SFrançois Tigeot } 36269edbd4a0SFrançois Tigeot 36279edbd4a0SFrançois Tigeot return count; 36289edbd4a0SFrançois Tigeot } 36299edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_speaker_allocation); 36309edbd4a0SFrançois Tigeot 36319edbd4a0SFrançois Tigeot /** 3632ba55f2f5SFrançois Tigeot * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay 36335718399fSFrançois Tigeot * @connector: connector associated with the HDMI/DP sink 36345718399fSFrançois Tigeot * @mode: the display mode 3635ba55f2f5SFrançois Tigeot * 3636ba55f2f5SFrançois Tigeot * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if 3637ba55f2f5SFrançois Tigeot * the sink doesn't support audio or video. 36385718399fSFrançois Tigeot */ 36395718399fSFrançois Tigeot int drm_av_sync_delay(struct drm_connector *connector, 3640352ff8bdSFrançois Tigeot const struct drm_display_mode *mode) 36415718399fSFrançois Tigeot { 36425718399fSFrançois Tigeot int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 36435718399fSFrançois Tigeot int a, v; 36445718399fSFrançois Tigeot 36455718399fSFrançois Tigeot if (!connector->latency_present[0]) 36465718399fSFrançois Tigeot return 0; 36475718399fSFrançois Tigeot if (!connector->latency_present[1]) 36485718399fSFrançois Tigeot i = 0; 36495718399fSFrançois Tigeot 36505718399fSFrançois Tigeot a = connector->audio_latency[i]; 36515718399fSFrançois Tigeot v = connector->video_latency[i]; 36525718399fSFrançois Tigeot 36535718399fSFrançois Tigeot /* 36545718399fSFrançois Tigeot * HDMI/DP sink doesn't support audio or video? 36555718399fSFrançois Tigeot */ 36565718399fSFrançois Tigeot if (a == 255 || v == 255) 36575718399fSFrançois Tigeot return 0; 36585718399fSFrançois Tigeot 36595718399fSFrançois Tigeot /* 36605718399fSFrançois Tigeot * Convert raw EDID values to millisecond. 36615718399fSFrançois Tigeot * Treat unknown latency as 0ms. 36625718399fSFrançois Tigeot */ 36635718399fSFrançois Tigeot if (a) 36645718399fSFrançois Tigeot a = min(2 * (a - 1), 500); 36655718399fSFrançois Tigeot if (v) 36665718399fSFrançois Tigeot v = min(2 * (v - 1), 500); 36675718399fSFrançois Tigeot 36685718399fSFrançois Tigeot return max(v - a, 0); 36695718399fSFrançois Tigeot } 36706e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_av_sync_delay); 36715718399fSFrançois Tigeot 36725718399fSFrançois Tigeot /** 36735718399fSFrançois Tigeot * drm_select_eld - select one ELD from multiple HDMI/DP sinks 36745718399fSFrançois Tigeot * @encoder: the encoder just changed display mode 36755718399fSFrançois Tigeot * 36765718399fSFrançois Tigeot * It's possible for one encoder to be associated with multiple HDMI/DP sinks. 36775718399fSFrançois Tigeot * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. 3678ba55f2f5SFrançois Tigeot * 3679ba55f2f5SFrançois Tigeot * Return: The connector associated with the first HDMI/DP sink that has ELD 3680ba55f2f5SFrançois Tigeot * attached to it. 36815718399fSFrançois Tigeot */ 3682352ff8bdSFrançois Tigeot struct drm_connector *drm_select_eld(struct drm_encoder *encoder) 36835718399fSFrançois Tigeot { 36845718399fSFrançois Tigeot struct drm_connector *connector; 36855718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 36865718399fSFrançois Tigeot 3687ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 368824edb884SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 3689ba55f2f5SFrançois Tigeot 3690a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) 36915718399fSFrançois Tigeot if (connector->encoder == encoder && connector->eld[0]) 36925718399fSFrançois Tigeot return connector; 36935718399fSFrançois Tigeot 36945718399fSFrançois Tigeot return NULL; 36955718399fSFrançois Tigeot } 36966e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_select_eld); 36975718399fSFrançois Tigeot 36985718399fSFrançois Tigeot /** 3699ba55f2f5SFrançois Tigeot * drm_detect_hdmi_monitor - detect whether monitor is HDMI 37005718399fSFrançois Tigeot * @edid: monitor EDID information 37015718399fSFrançois Tigeot * 37025718399fSFrançois Tigeot * Parse the CEA extension according to CEA-861-B. 3703ba55f2f5SFrançois Tigeot * 3704ba55f2f5SFrançois Tigeot * Return: True if the monitor is HDMI, false if not or unknown. 37055718399fSFrançois Tigeot */ 37065718399fSFrançois Tigeot bool drm_detect_hdmi_monitor(struct edid *edid) 37075718399fSFrançois Tigeot { 37085718399fSFrançois Tigeot u8 *edid_ext; 37096e29dde8SFrançois Tigeot int i; 37105718399fSFrançois Tigeot int start_offset, end_offset; 37115718399fSFrançois Tigeot 37125718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 37135718399fSFrançois Tigeot if (!edid_ext) 37146e29dde8SFrançois Tigeot return false; 37155718399fSFrançois Tigeot 37166e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 37176e29dde8SFrançois Tigeot return false; 37185718399fSFrançois Tigeot 37195718399fSFrançois Tigeot /* 37205718399fSFrançois Tigeot * Because HDMI identifier is in Vendor Specific Block, 37215718399fSFrançois Tigeot * search it from all data blocks of CEA extension. 37225718399fSFrançois Tigeot */ 37236e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 37246e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(&edid_ext[i])) 37256e29dde8SFrançois Tigeot return true; 37265718399fSFrançois Tigeot } 37275718399fSFrançois Tigeot 37286e29dde8SFrançois Tigeot return false; 37295718399fSFrançois Tigeot } 37306e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_hdmi_monitor); 37315718399fSFrançois Tigeot 37325718399fSFrançois Tigeot /** 37335718399fSFrançois Tigeot * drm_detect_monitor_audio - check monitor audio capability 3734ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 37355718399fSFrançois Tigeot * 37365718399fSFrançois Tigeot * Monitor should have CEA extension block. 37375718399fSFrançois Tigeot * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic 37385718399fSFrançois Tigeot * audio' only. If there is any audio extension block and supported 37395718399fSFrançois Tigeot * audio format, assume at least 'basic audio' support, even if 'basic 37405718399fSFrançois Tigeot * audio' is not defined in EDID. 37415718399fSFrançois Tigeot * 3742ba55f2f5SFrançois Tigeot * Return: True if the monitor supports audio, false otherwise. 37435718399fSFrançois Tigeot */ 37445718399fSFrançois Tigeot bool drm_detect_monitor_audio(struct edid *edid) 37455718399fSFrançois Tigeot { 37465718399fSFrançois Tigeot u8 *edid_ext; 37475718399fSFrançois Tigeot int i, j; 37485718399fSFrançois Tigeot bool has_audio = false; 37495718399fSFrançois Tigeot int start_offset, end_offset; 37505718399fSFrançois Tigeot 37515718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 37525718399fSFrançois Tigeot if (!edid_ext) 37535718399fSFrançois Tigeot goto end; 37545718399fSFrançois Tigeot 37555718399fSFrançois Tigeot has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0); 37565718399fSFrançois Tigeot 37575718399fSFrançois Tigeot if (has_audio) { 37585718399fSFrançois Tigeot DRM_DEBUG_KMS("Monitor has basic audio support\n"); 37595718399fSFrançois Tigeot goto end; 37605718399fSFrançois Tigeot } 37615718399fSFrançois Tigeot 37626e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 37636e29dde8SFrançois Tigeot goto end; 37645718399fSFrançois Tigeot 37656e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 37666e29dde8SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { 37675718399fSFrançois Tigeot has_audio = true; 37686e29dde8SFrançois Tigeot for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) 37695718399fSFrançois Tigeot DRM_DEBUG_KMS("CEA audio format %d\n", 37705718399fSFrançois Tigeot (edid_ext[i + j] >> 3) & 0xf); 37715718399fSFrançois Tigeot goto end; 37725718399fSFrançois Tigeot } 37735718399fSFrançois Tigeot } 37745718399fSFrançois Tigeot end: 37755718399fSFrançois Tigeot return has_audio; 37765718399fSFrançois Tigeot } 37776e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_monitor_audio); 37785718399fSFrançois Tigeot 37795718399fSFrançois Tigeot /** 3780a2fdbec6SFrançois Tigeot * drm_rgb_quant_range_selectable - is RGB quantization range selectable? 3781ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 3782a2fdbec6SFrançois Tigeot * 3783a2fdbec6SFrançois Tigeot * Check whether the monitor reports the RGB quantization range selection 3784a2fdbec6SFrançois Tigeot * as supported. The AVI infoframe can then be used to inform the monitor 3785a2fdbec6SFrançois Tigeot * which quantization range (full or limited) is used. 3786ba55f2f5SFrançois Tigeot * 3787ba55f2f5SFrançois Tigeot * Return: True if the RGB quantization range is selectable, false otherwise. 3788a2fdbec6SFrançois Tigeot */ 3789a2fdbec6SFrançois Tigeot bool drm_rgb_quant_range_selectable(struct edid *edid) 3790a2fdbec6SFrançois Tigeot { 3791a2fdbec6SFrançois Tigeot u8 *edid_ext; 3792a2fdbec6SFrançois Tigeot int i, start, end; 3793a2fdbec6SFrançois Tigeot 3794a2fdbec6SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 3795a2fdbec6SFrançois Tigeot if (!edid_ext) 3796a2fdbec6SFrançois Tigeot return false; 3797a2fdbec6SFrançois Tigeot 3798a2fdbec6SFrançois Tigeot if (cea_db_offsets(edid_ext, &start, &end)) 3799a2fdbec6SFrançois Tigeot return false; 3800a2fdbec6SFrançois Tigeot 3801a2fdbec6SFrançois Tigeot for_each_cea_db(edid_ext, i, start, end) { 3802a2fdbec6SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && 3803a2fdbec6SFrançois Tigeot cea_db_payload_len(&edid_ext[i]) == 2) { 3804a2fdbec6SFrançois Tigeot DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); 3805a2fdbec6SFrançois Tigeot return edid_ext[i + 2] & EDID_CEA_VCDB_QS; 3806a2fdbec6SFrançois Tigeot } 3807a2fdbec6SFrançois Tigeot } 3808a2fdbec6SFrançois Tigeot 3809a2fdbec6SFrançois Tigeot return false; 3810a2fdbec6SFrançois Tigeot } 3811a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_rgb_quant_range_selectable); 3812a2fdbec6SFrançois Tigeot 3813a2fdbec6SFrançois Tigeot /** 3814ba55f2f5SFrançois Tigeot * drm_assign_hdmi_deep_color_info - detect whether monitor supports 3815ba55f2f5SFrançois Tigeot * hdmi deep color modes and update drm_display_info if so. 3816ba55f2f5SFrançois Tigeot * @edid: monitor EDID information 3817ba55f2f5SFrançois Tigeot * @info: Updated with maximum supported deep color bpc and color format 3818ba55f2f5SFrançois Tigeot * if deep color supported. 38191b13d190SFrançois Tigeot * @connector: DRM connector, used only for debug output 3820ba55f2f5SFrançois Tigeot * 3821ba55f2f5SFrançois Tigeot * Parse the CEA extension according to CEA-861-B. 3822ba55f2f5SFrançois Tigeot * Return true if HDMI deep color supported, false if not or unknown. 3823ba55f2f5SFrançois Tigeot */ 3824ba55f2f5SFrançois Tigeot static bool drm_assign_hdmi_deep_color_info(struct edid *edid, 3825ba55f2f5SFrançois Tigeot struct drm_display_info *info, 3826ba55f2f5SFrançois Tigeot struct drm_connector *connector) 3827ba55f2f5SFrançois Tigeot { 3828ba55f2f5SFrançois Tigeot u8 *edid_ext, *hdmi; 3829ba55f2f5SFrançois Tigeot int i; 3830ba55f2f5SFrançois Tigeot int start_offset, end_offset; 3831ba55f2f5SFrançois Tigeot unsigned int dc_bpc = 0; 3832ba55f2f5SFrançois Tigeot 3833ba55f2f5SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 3834ba55f2f5SFrançois Tigeot if (!edid_ext) 3835ba55f2f5SFrançois Tigeot return false; 3836ba55f2f5SFrançois Tigeot 3837ba55f2f5SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 3838ba55f2f5SFrançois Tigeot return false; 3839ba55f2f5SFrançois Tigeot 3840ba55f2f5SFrançois Tigeot /* 3841ba55f2f5SFrançois Tigeot * Because HDMI identifier is in Vendor Specific Block, 3842ba55f2f5SFrançois Tigeot * search it from all data blocks of CEA extension. 3843ba55f2f5SFrançois Tigeot */ 3844ba55f2f5SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 3845ba55f2f5SFrançois Tigeot if (cea_db_is_hdmi_vsdb(&edid_ext[i])) { 3846ba55f2f5SFrançois Tigeot /* HDMI supports at least 8 bpc */ 3847ba55f2f5SFrançois Tigeot info->bpc = 8; 3848ba55f2f5SFrançois Tigeot 3849ba55f2f5SFrançois Tigeot hdmi = &edid_ext[i]; 3850ba55f2f5SFrançois Tigeot if (cea_db_payload_len(hdmi) < 6) 3851ba55f2f5SFrançois Tigeot return false; 3852ba55f2f5SFrançois Tigeot 3853ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_30) { 3854ba55f2f5SFrançois Tigeot dc_bpc = 10; 3855c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30; 3856ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 30.\n", 3857ba55f2f5SFrançois Tigeot connector->name); 3858ba55f2f5SFrançois Tigeot } 3859ba55f2f5SFrançois Tigeot 3860ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_36) { 3861ba55f2f5SFrançois Tigeot dc_bpc = 12; 3862c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36; 3863ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 36.\n", 3864ba55f2f5SFrançois Tigeot connector->name); 3865ba55f2f5SFrançois Tigeot } 3866ba55f2f5SFrançois Tigeot 3867ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_48) { 3868ba55f2f5SFrançois Tigeot dc_bpc = 16; 3869c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48; 3870ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 48.\n", 3871ba55f2f5SFrançois Tigeot connector->name); 3872ba55f2f5SFrançois Tigeot } 3873ba55f2f5SFrançois Tigeot 3874ba55f2f5SFrançois Tigeot if (dc_bpc > 0) { 3875ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n", 3876ba55f2f5SFrançois Tigeot connector->name, dc_bpc); 3877ba55f2f5SFrançois Tigeot info->bpc = dc_bpc; 3878ba55f2f5SFrançois Tigeot 3879ba55f2f5SFrançois Tigeot /* 3880ba55f2f5SFrançois Tigeot * Deep color support mandates RGB444 support for all video 3881ba55f2f5SFrançois Tigeot * modes and forbids YCRCB422 support for all video modes per 3882ba55f2f5SFrançois Tigeot * HDMI 1.3 spec. 3883ba55f2f5SFrançois Tigeot */ 3884ba55f2f5SFrançois Tigeot info->color_formats = DRM_COLOR_FORMAT_RGB444; 3885ba55f2f5SFrançois Tigeot 3886ba55f2f5SFrançois Tigeot /* YCRCB444 is optional according to spec. */ 3887ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) { 3888ba55f2f5SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 3889ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n", 3890ba55f2f5SFrançois Tigeot connector->name); 3891ba55f2f5SFrançois Tigeot } 3892ba55f2f5SFrançois Tigeot 3893ba55f2f5SFrançois Tigeot /* 3894ba55f2f5SFrançois Tigeot * Spec says that if any deep color mode is supported at all, 3895ba55f2f5SFrançois Tigeot * then deep color 36 bit must be supported. 3896ba55f2f5SFrançois Tigeot */ 3897ba55f2f5SFrançois Tigeot if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) { 3898ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n", 3899ba55f2f5SFrançois Tigeot connector->name); 3900ba55f2f5SFrançois Tigeot } 3901ba55f2f5SFrançois Tigeot 3902ba55f2f5SFrançois Tigeot return true; 3903ba55f2f5SFrançois Tigeot } 3904ba55f2f5SFrançois Tigeot else { 3905ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: No deep color support on this HDMI sink.\n", 3906ba55f2f5SFrançois Tigeot connector->name); 3907ba55f2f5SFrançois Tigeot } 3908ba55f2f5SFrançois Tigeot } 3909ba55f2f5SFrançois Tigeot } 3910ba55f2f5SFrançois Tigeot 3911ba55f2f5SFrançois Tigeot return false; 3912ba55f2f5SFrançois Tigeot } 3913ba55f2f5SFrançois Tigeot 3914ba55f2f5SFrançois Tigeot /** 39155718399fSFrançois Tigeot * drm_add_display_info - pull display info out if present 39165718399fSFrançois Tigeot * @edid: EDID data 39175718399fSFrançois Tigeot * @info: display info (attached to connector) 3918ba55f2f5SFrançois Tigeot * @connector: connector whose edid is used to build display info 39195718399fSFrançois Tigeot * 39205718399fSFrançois Tigeot * Grab any available display info and stuff it into the drm_display_info 39215718399fSFrançois Tigeot * structure that's part of the connector. Useful for tracking bpp and 39225718399fSFrançois Tigeot * color spaces. 39235718399fSFrançois Tigeot */ 39245718399fSFrançois Tigeot static void drm_add_display_info(struct edid *edid, 3925ba55f2f5SFrançois Tigeot struct drm_display_info *info, 3926ba55f2f5SFrançois Tigeot struct drm_connector *connector) 39275718399fSFrançois Tigeot { 39285718399fSFrançois Tigeot u8 *edid_ext; 39295718399fSFrançois Tigeot 39305718399fSFrançois Tigeot info->width_mm = edid->width_cm * 10; 39315718399fSFrançois Tigeot info->height_mm = edid->height_cm * 10; 39325718399fSFrançois Tigeot 39335718399fSFrançois Tigeot /* driver figures it out in this case */ 39345718399fSFrançois Tigeot info->bpc = 0; 39355718399fSFrançois Tigeot info->color_formats = 0; 39365718399fSFrançois Tigeot 39376e29dde8SFrançois Tigeot if (edid->revision < 3) 39385718399fSFrançois Tigeot return; 39395718399fSFrançois Tigeot 39405718399fSFrançois Tigeot if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) 39415718399fSFrançois Tigeot return; 39425718399fSFrançois Tigeot 39436e29dde8SFrançois Tigeot /* Get data from CEA blocks if present */ 39446e29dde8SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 39456e29dde8SFrançois Tigeot if (edid_ext) { 39466e29dde8SFrançois Tigeot info->cea_rev = edid_ext[1]; 39476e29dde8SFrançois Tigeot 39486e29dde8SFrançois Tigeot /* The existence of a CEA block should imply RGB support */ 39496e29dde8SFrançois Tigeot info->color_formats = DRM_COLOR_FORMAT_RGB444; 39506e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB444) 39516e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 39526e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB422) 39536e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 39546e29dde8SFrançois Tigeot } 39556e29dde8SFrançois Tigeot 3956ba55f2f5SFrançois Tigeot /* HDMI deep color modes supported? Assign to info, if so */ 3957ba55f2f5SFrançois Tigeot drm_assign_hdmi_deep_color_info(edid, info, connector); 3958ba55f2f5SFrançois Tigeot 39596e29dde8SFrançois Tigeot /* Only defined for 1.4 with digital displays */ 39606e29dde8SFrançois Tigeot if (edid->revision < 4) 39616e29dde8SFrançois Tigeot return; 39626e29dde8SFrançois Tigeot 39635718399fSFrançois Tigeot switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { 39645718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_6: 39655718399fSFrançois Tigeot info->bpc = 6; 39665718399fSFrançois Tigeot break; 39675718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_8: 39685718399fSFrançois Tigeot info->bpc = 8; 39695718399fSFrançois Tigeot break; 39705718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_10: 39715718399fSFrançois Tigeot info->bpc = 10; 39725718399fSFrançois Tigeot break; 39735718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_12: 39745718399fSFrançois Tigeot info->bpc = 12; 39755718399fSFrançois Tigeot break; 39765718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_14: 39775718399fSFrançois Tigeot info->bpc = 14; 39785718399fSFrançois Tigeot break; 39795718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_16: 39805718399fSFrançois Tigeot info->bpc = 16; 39815718399fSFrançois Tigeot break; 39825718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_UNDEF: 39835718399fSFrançois Tigeot default: 39845718399fSFrançois Tigeot info->bpc = 0; 39855718399fSFrançois Tigeot break; 39865718399fSFrançois Tigeot } 39875718399fSFrançois Tigeot 3988ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n", 3989ba55f2f5SFrançois Tigeot connector->name, info->bpc); 3990ba55f2f5SFrançois Tigeot 39916e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_RGB444; 39926e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444) 39936e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 39946e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) 39956e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 39965718399fSFrançois Tigeot } 39975718399fSFrançois Tigeot 39988621f407SFrançois Tigeot static int validate_displayid(u8 *displayid, int length, int idx) 39998621f407SFrançois Tigeot { 40008621f407SFrançois Tigeot int i; 40018621f407SFrançois Tigeot u8 csum = 0; 40028621f407SFrançois Tigeot struct displayid_hdr *base; 40038621f407SFrançois Tigeot 40048621f407SFrançois Tigeot base = (struct displayid_hdr *)&displayid[idx]; 40058621f407SFrançois Tigeot 40068621f407SFrançois Tigeot DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", 40078621f407SFrançois Tigeot base->rev, base->bytes, base->prod_id, base->ext_count); 40088621f407SFrançois Tigeot 40098621f407SFrançois Tigeot if (base->bytes + 5 > length - idx) 40108621f407SFrançois Tigeot return -EINVAL; 40118621f407SFrançois Tigeot for (i = idx; i <= base->bytes + 5; i++) { 40128621f407SFrançois Tigeot csum += displayid[i]; 40138621f407SFrançois Tigeot } 40148621f407SFrançois Tigeot if (csum) { 40158621f407SFrançois Tigeot DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum); 40168621f407SFrançois Tigeot return -EINVAL; 40178621f407SFrançois Tigeot } 40188621f407SFrançois Tigeot return 0; 40198621f407SFrançois Tigeot } 40208621f407SFrançois Tigeot 40218621f407SFrançois Tigeot static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, 40228621f407SFrançois Tigeot struct displayid_detailed_timings_1 *timings) 40238621f407SFrançois Tigeot { 40248621f407SFrançois Tigeot struct drm_display_mode *mode; 40258621f407SFrançois Tigeot unsigned pixel_clock = (timings->pixel_clock[0] | 40268621f407SFrançois Tigeot (timings->pixel_clock[1] << 8) | 40278621f407SFrançois Tigeot (timings->pixel_clock[2] << 16)); 40288621f407SFrançois Tigeot unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1; 40298621f407SFrançois Tigeot unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1; 40308621f407SFrançois Tigeot unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1; 40318621f407SFrançois Tigeot unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1; 40328621f407SFrançois Tigeot unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1; 40338621f407SFrançois Tigeot unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1; 40348621f407SFrançois Tigeot unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1; 40358621f407SFrançois Tigeot unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1; 40368621f407SFrançois Tigeot bool hsync_positive = (timings->hsync[1] >> 7) & 0x1; 40378621f407SFrançois Tigeot bool vsync_positive = (timings->vsync[1] >> 7) & 0x1; 40388621f407SFrançois Tigeot mode = drm_mode_create(dev); 40398621f407SFrançois Tigeot if (!mode) 40408621f407SFrançois Tigeot return NULL; 40418621f407SFrançois Tigeot 40428621f407SFrançois Tigeot mode->clock = pixel_clock * 10; 40438621f407SFrançois Tigeot mode->hdisplay = hactive; 40448621f407SFrançois Tigeot mode->hsync_start = mode->hdisplay + hsync; 40458621f407SFrançois Tigeot mode->hsync_end = mode->hsync_start + hsync_width; 40468621f407SFrançois Tigeot mode->htotal = mode->hdisplay + hblank; 40478621f407SFrançois Tigeot 40488621f407SFrançois Tigeot mode->vdisplay = vactive; 40498621f407SFrançois Tigeot mode->vsync_start = mode->vdisplay + vsync; 40508621f407SFrançois Tigeot mode->vsync_end = mode->vsync_start + vsync_width; 40518621f407SFrançois Tigeot mode->vtotal = mode->vdisplay + vblank; 40528621f407SFrançois Tigeot 40538621f407SFrançois Tigeot mode->flags = 0; 40548621f407SFrançois Tigeot mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 40558621f407SFrançois Tigeot mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 40568621f407SFrançois Tigeot mode->type = DRM_MODE_TYPE_DRIVER; 40578621f407SFrançois Tigeot 40588621f407SFrançois Tigeot if (timings->flags & 0x80) 40598621f407SFrançois Tigeot mode->type |= DRM_MODE_TYPE_PREFERRED; 40608621f407SFrançois Tigeot mode->vrefresh = drm_mode_vrefresh(mode); 40618621f407SFrançois Tigeot drm_mode_set_name(mode); 40628621f407SFrançois Tigeot 40638621f407SFrançois Tigeot return mode; 40648621f407SFrançois Tigeot } 40658621f407SFrançois Tigeot 40668621f407SFrançois Tigeot static int add_displayid_detailed_1_modes(struct drm_connector *connector, 40678621f407SFrançois Tigeot struct displayid_block *block) 40688621f407SFrançois Tigeot { 40698621f407SFrançois Tigeot struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block; 40708621f407SFrançois Tigeot int i; 40718621f407SFrançois Tigeot int num_timings; 40728621f407SFrançois Tigeot struct drm_display_mode *newmode; 40738621f407SFrançois Tigeot int num_modes = 0; 40748621f407SFrançois Tigeot /* blocks must be multiple of 20 bytes length */ 40758621f407SFrançois Tigeot if (block->num_bytes % 20) 40768621f407SFrançois Tigeot return 0; 40778621f407SFrançois Tigeot 40788621f407SFrançois Tigeot num_timings = block->num_bytes / 20; 40798621f407SFrançois Tigeot for (i = 0; i < num_timings; i++) { 40808621f407SFrançois Tigeot struct displayid_detailed_timings_1 *timings = &det->timings[i]; 40818621f407SFrançois Tigeot 40828621f407SFrançois Tigeot newmode = drm_mode_displayid_detailed(connector->dev, timings); 40838621f407SFrançois Tigeot if (!newmode) 40848621f407SFrançois Tigeot continue; 40858621f407SFrançois Tigeot 40868621f407SFrançois Tigeot drm_mode_probed_add(connector, newmode); 40878621f407SFrançois Tigeot num_modes++; 40888621f407SFrançois Tigeot } 40898621f407SFrançois Tigeot return num_modes; 40908621f407SFrançois Tigeot } 40918621f407SFrançois Tigeot 40928621f407SFrançois Tigeot static int add_displayid_detailed_modes(struct drm_connector *connector, 40938621f407SFrançois Tigeot struct edid *edid) 40948621f407SFrançois Tigeot { 40958621f407SFrançois Tigeot u8 *displayid; 40968621f407SFrançois Tigeot int ret; 40978621f407SFrançois Tigeot int idx = 1; 40988621f407SFrançois Tigeot int length = EDID_LENGTH; 40998621f407SFrançois Tigeot struct displayid_block *block; 41008621f407SFrançois Tigeot int num_modes = 0; 41018621f407SFrançois Tigeot 41028621f407SFrançois Tigeot displayid = drm_find_displayid_extension(edid); 41038621f407SFrançois Tigeot if (!displayid) 41048621f407SFrançois Tigeot return 0; 41058621f407SFrançois Tigeot 41068621f407SFrançois Tigeot ret = validate_displayid(displayid, length, idx); 41078621f407SFrançois Tigeot if (ret) 41088621f407SFrançois Tigeot return 0; 41098621f407SFrançois Tigeot 41108621f407SFrançois Tigeot idx += sizeof(struct displayid_hdr); 41118621f407SFrançois Tigeot while (block = (struct displayid_block *)&displayid[idx], 41128621f407SFrançois Tigeot idx + sizeof(struct displayid_block) <= length && 41138621f407SFrançois Tigeot idx + sizeof(struct displayid_block) + block->num_bytes <= length && 41148621f407SFrançois Tigeot block->num_bytes > 0) { 41158621f407SFrançois Tigeot idx += block->num_bytes + sizeof(struct displayid_block); 41168621f407SFrançois Tigeot switch (block->tag) { 41178621f407SFrançois Tigeot case DATA_BLOCK_TYPE_1_DETAILED_TIMING: 41188621f407SFrançois Tigeot num_modes += add_displayid_detailed_1_modes(connector, block); 41198621f407SFrançois Tigeot break; 41208621f407SFrançois Tigeot } 41218621f407SFrançois Tigeot } 41228621f407SFrançois Tigeot return num_modes; 41238621f407SFrançois Tigeot } 41248621f407SFrançois Tigeot 41255718399fSFrançois Tigeot /** 41265718399fSFrançois Tigeot * drm_add_edid_modes - add modes from EDID data, if available 41275718399fSFrançois Tigeot * @connector: connector we're probing 4128ba55f2f5SFrançois Tigeot * @edid: EDID data 41295718399fSFrançois Tigeot * 41305718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. 41315718399fSFrançois Tigeot * 4132ba55f2f5SFrançois Tigeot * Return: The number of modes added or 0 if we couldn't find any. 41335718399fSFrançois Tigeot */ 41345718399fSFrançois Tigeot int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) 41355718399fSFrançois Tigeot { 41365718399fSFrançois Tigeot int num_modes = 0; 41375718399fSFrançois Tigeot u32 quirks; 41385718399fSFrançois Tigeot 41395718399fSFrançois Tigeot if (edid == NULL) { 41405718399fSFrançois Tigeot return 0; 41415718399fSFrançois Tigeot } 41425718399fSFrançois Tigeot if (!drm_edid_is_valid(edid)) { 41436e29dde8SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID invalid.\n", 4144ba55f2f5SFrançois Tigeot connector->name); 41455718399fSFrançois Tigeot return 0; 41465718399fSFrançois Tigeot } 41475718399fSFrançois Tigeot 41485718399fSFrançois Tigeot quirks = edid_get_quirks(edid); 41495718399fSFrançois Tigeot 41505718399fSFrançois Tigeot /* 41515718399fSFrançois Tigeot * EDID spec says modes should be preferred in this order: 41525718399fSFrançois Tigeot * - preferred detailed mode 41535718399fSFrançois Tigeot * - other detailed modes from base block 41545718399fSFrançois Tigeot * - detailed modes from extension blocks 41555718399fSFrançois Tigeot * - CVT 3-byte code modes 41565718399fSFrançois Tigeot * - standard timing codes 41575718399fSFrançois Tigeot * - established timing codes 41585718399fSFrançois Tigeot * - modes inferred from GTF or CVT range information 41595718399fSFrançois Tigeot * 41605718399fSFrançois Tigeot * We get this pretty much right. 41615718399fSFrançois Tigeot * 41625718399fSFrançois Tigeot * XXX order for additional mode types in extension blocks? 41635718399fSFrançois Tigeot */ 41645718399fSFrançois Tigeot num_modes += add_detailed_modes(connector, edid, quirks); 41655718399fSFrançois Tigeot num_modes += add_cvt_modes(connector, edid); 41665718399fSFrançois Tigeot num_modes += add_standard_modes(connector, edid); 41675718399fSFrançois Tigeot num_modes += add_established_modes(connector, edid); 41686e29dde8SFrançois Tigeot num_modes += add_cea_modes(connector, edid); 41699edbd4a0SFrançois Tigeot num_modes += add_alternate_cea_modes(connector, edid); 41708621f407SFrançois Tigeot num_modes += add_displayid_detailed_modes(connector, edid); 417119c468b4SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) 417219c468b4SFrançois Tigeot num_modes += add_inferred_modes(connector, edid); 41735718399fSFrançois Tigeot 41745718399fSFrançois Tigeot if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) 41755718399fSFrançois Tigeot edid_fixup_preferred(connector, quirks); 41765718399fSFrançois Tigeot 4177ba55f2f5SFrançois Tigeot drm_add_display_info(edid, &connector->display_info, connector); 41785718399fSFrançois Tigeot 41798621f407SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_6BPC) 41808621f407SFrançois Tigeot connector->display_info.bpc = 6; 41818621f407SFrançois Tigeot 41829edbd4a0SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_8BPC) 41839edbd4a0SFrançois Tigeot connector->display_info.bpc = 8; 41849edbd4a0SFrançois Tigeot 4185ba55f2f5SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_12BPC) 4186ba55f2f5SFrançois Tigeot connector->display_info.bpc = 12; 4187ba55f2f5SFrançois Tigeot 41885718399fSFrançois Tigeot return num_modes; 41895718399fSFrançois Tigeot } 41906e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_edid_modes); 41915718399fSFrançois Tigeot 41925718399fSFrançois Tigeot /** 41935718399fSFrançois Tigeot * drm_add_modes_noedid - add modes for the connectors without EDID 41945718399fSFrançois Tigeot * @connector: connector we're probing 41955718399fSFrançois Tigeot * @hdisplay: the horizontal display limit 41965718399fSFrançois Tigeot * @vdisplay: the vertical display limit 41975718399fSFrançois Tigeot * 41985718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. Only when the 41995718399fSFrançois Tigeot * hdisplay/vdisplay is not beyond the given limit, it will be added. 42005718399fSFrançois Tigeot * 4201ba55f2f5SFrançois Tigeot * Return: The number of modes added or 0 if we couldn't find any. 42025718399fSFrançois Tigeot */ 42035718399fSFrançois Tigeot int drm_add_modes_noedid(struct drm_connector *connector, 42045718399fSFrançois Tigeot int hdisplay, int vdisplay) 42055718399fSFrançois Tigeot { 42065718399fSFrançois Tigeot int i, count, num_modes = 0; 42075718399fSFrançois Tigeot struct drm_display_mode *mode; 42085718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 42095718399fSFrançois Tigeot 4210a05eeebfSFrançois Tigeot count = ARRAY_SIZE(drm_dmt_modes); 42115718399fSFrançois Tigeot if (hdisplay < 0) 42125718399fSFrançois Tigeot hdisplay = 0; 42135718399fSFrançois Tigeot if (vdisplay < 0) 42145718399fSFrançois Tigeot vdisplay = 0; 42155718399fSFrançois Tigeot 42165718399fSFrançois Tigeot for (i = 0; i < count; i++) { 4217ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 42185718399fSFrançois Tigeot if (hdisplay && vdisplay) { 42195718399fSFrançois Tigeot /* 42205718399fSFrançois Tigeot * Only when two are valid, they will be used to check 42215718399fSFrançois Tigeot * whether the mode should be added to the mode list of 42225718399fSFrançois Tigeot * the connector. 42235718399fSFrançois Tigeot */ 42245718399fSFrançois Tigeot if (ptr->hdisplay > hdisplay || 42255718399fSFrançois Tigeot ptr->vdisplay > vdisplay) 42265718399fSFrançois Tigeot continue; 42275718399fSFrançois Tigeot } 42285718399fSFrançois Tigeot if (drm_mode_vrefresh(ptr) > 61) 42295718399fSFrançois Tigeot continue; 42305718399fSFrançois Tigeot mode = drm_mode_duplicate(dev, ptr); 42315718399fSFrançois Tigeot if (mode) { 42325718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 42335718399fSFrançois Tigeot num_modes++; 42345718399fSFrançois Tigeot } 42355718399fSFrançois Tigeot } 42365718399fSFrançois Tigeot return num_modes; 42375718399fSFrançois Tigeot } 42386e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_modes_noedid); 423987cc1051SMichael Neumann 4240ba55f2f5SFrançois Tigeot /** 4241ba55f2f5SFrançois Tigeot * drm_set_preferred_mode - Sets the preferred mode of a connector 4242ba55f2f5SFrançois Tigeot * @connector: connector whose mode list should be processed 4243ba55f2f5SFrançois Tigeot * @hpref: horizontal resolution of preferred mode 4244ba55f2f5SFrançois Tigeot * @vpref: vertical resolution of preferred mode 4245ba55f2f5SFrançois Tigeot * 4246ba55f2f5SFrançois Tigeot * Marks a mode as preferred if it matches the resolution specified by @hpref 4247ba55f2f5SFrançois Tigeot * and @vpref. 4248ba55f2f5SFrançois Tigeot */ 42499edbd4a0SFrançois Tigeot void drm_set_preferred_mode(struct drm_connector *connector, 42509edbd4a0SFrançois Tigeot int hpref, int vpref) 42519edbd4a0SFrançois Tigeot { 42529edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 42539edbd4a0SFrançois Tigeot 42549edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 4255ba55f2f5SFrançois Tigeot if (mode->hdisplay == hpref && 4256ba55f2f5SFrançois Tigeot mode->vdisplay == vpref) 42579edbd4a0SFrançois Tigeot mode->type |= DRM_MODE_TYPE_PREFERRED; 42589edbd4a0SFrançois Tigeot } 42599edbd4a0SFrançois Tigeot } 42609edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_set_preferred_mode); 42619edbd4a0SFrançois Tigeot 426287cc1051SMichael Neumann /** 426387cc1051SMichael Neumann * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with 426487cc1051SMichael Neumann * data from a DRM display mode 426587cc1051SMichael Neumann * @frame: HDMI AVI infoframe 426687cc1051SMichael Neumann * @mode: DRM display mode 426787cc1051SMichael Neumann * 4268ba55f2f5SFrançois Tigeot * Return: 0 on success or a negative error code on failure. 426987cc1051SMichael Neumann */ 427087cc1051SMichael Neumann int 427187cc1051SMichael Neumann drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, 427287cc1051SMichael Neumann const struct drm_display_mode *mode) 427387cc1051SMichael Neumann { 427487cc1051SMichael Neumann int err; 427587cc1051SMichael Neumann 427687cc1051SMichael Neumann if (!frame || !mode) 427787cc1051SMichael Neumann return -EINVAL; 427887cc1051SMichael Neumann 427987cc1051SMichael Neumann err = hdmi_avi_infoframe_init(frame); 428087cc1051SMichael Neumann if (err < 0) 428187cc1051SMichael Neumann return err; 428287cc1051SMichael Neumann 42839edbd4a0SFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLCLK) 42849edbd4a0SFrançois Tigeot frame->pixel_repeat = 1; 42859edbd4a0SFrançois Tigeot 428687cc1051SMichael Neumann frame->video_code = drm_match_cea_mode(mode); 428787cc1051SMichael Neumann 428887cc1051SMichael Neumann frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; 4289ba55f2f5SFrançois Tigeot 429024edb884SFrançois Tigeot /* 429124edb884SFrançois Tigeot * Populate picture aspect ratio from either 429224edb884SFrançois Tigeot * user input (if specified) or from the CEA mode list. 429324edb884SFrançois Tigeot */ 429424edb884SFrançois Tigeot if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || 429524edb884SFrançois Tigeot mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) 429624edb884SFrançois Tigeot frame->picture_aspect = mode->picture_aspect_ratio; 429724edb884SFrançois Tigeot else if (frame->video_code > 0) 4298ba55f2f5SFrançois Tigeot frame->picture_aspect = drm_get_cea_aspect_ratio( 4299ba55f2f5SFrançois Tigeot frame->video_code); 4300ba55f2f5SFrançois Tigeot 430187cc1051SMichael Neumann frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; 4302ba55f2f5SFrançois Tigeot frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; 430387cc1051SMichael Neumann 430487cc1051SMichael Neumann return 0; 430587cc1051SMichael Neumann } 430687cc1051SMichael Neumann EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); 43079edbd4a0SFrançois Tigeot 43089edbd4a0SFrançois Tigeot static enum hdmi_3d_structure 43099edbd4a0SFrançois Tigeot s3d_structure_from_display_mode(const struct drm_display_mode *mode) 43109edbd4a0SFrançois Tigeot { 43119edbd4a0SFrançois Tigeot u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK; 43129edbd4a0SFrançois Tigeot 43139edbd4a0SFrançois Tigeot switch (layout) { 43149edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FRAME_PACKING: 43159edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FRAME_PACKING; 43169edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: 43179edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE; 43189edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: 43199edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE; 43209edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: 43219edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL; 43229edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH: 43239edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH; 43249edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: 43259edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; 43269edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: 43279edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM; 43289edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: 43299edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF; 43309edbd4a0SFrançois Tigeot default: 43319edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_INVALID; 43329edbd4a0SFrançois Tigeot } 43339edbd4a0SFrançois Tigeot } 43349edbd4a0SFrançois Tigeot 43359edbd4a0SFrançois Tigeot /** 43369edbd4a0SFrançois Tigeot * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with 43379edbd4a0SFrançois Tigeot * data from a DRM display mode 43389edbd4a0SFrançois Tigeot * @frame: HDMI vendor infoframe 43399edbd4a0SFrançois Tigeot * @mode: DRM display mode 43409edbd4a0SFrançois Tigeot * 43419edbd4a0SFrançois Tigeot * Note that there's is a need to send HDMI vendor infoframes only when using a 43429edbd4a0SFrançois Tigeot * 4k or stereoscopic 3D mode. So when giving any other mode as input this 43439edbd4a0SFrançois Tigeot * function will return -EINVAL, error that can be safely ignored. 43449edbd4a0SFrançois Tigeot * 4345ba55f2f5SFrançois Tigeot * Return: 0 on success or a negative error code on failure. 43469edbd4a0SFrançois Tigeot */ 43479edbd4a0SFrançois Tigeot int 43489edbd4a0SFrançois Tigeot drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, 43499edbd4a0SFrançois Tigeot const struct drm_display_mode *mode) 43509edbd4a0SFrançois Tigeot { 43519edbd4a0SFrançois Tigeot int err; 43529edbd4a0SFrançois Tigeot u32 s3d_flags; 43539edbd4a0SFrançois Tigeot u8 vic; 43549edbd4a0SFrançois Tigeot 43559edbd4a0SFrançois Tigeot if (!frame || !mode) 43569edbd4a0SFrançois Tigeot return -EINVAL; 43579edbd4a0SFrançois Tigeot 43589edbd4a0SFrançois Tigeot vic = drm_match_hdmi_mode(mode); 43599edbd4a0SFrançois Tigeot s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; 43609edbd4a0SFrançois Tigeot 43619edbd4a0SFrançois Tigeot if (!vic && !s3d_flags) 43629edbd4a0SFrançois Tigeot return -EINVAL; 43639edbd4a0SFrançois Tigeot 43649edbd4a0SFrançois Tigeot if (vic && s3d_flags) 43659edbd4a0SFrançois Tigeot return -EINVAL; 43669edbd4a0SFrançois Tigeot 43679edbd4a0SFrançois Tigeot err = hdmi_vendor_infoframe_init(frame); 43689edbd4a0SFrançois Tigeot if (err < 0) 43699edbd4a0SFrançois Tigeot return err; 43709edbd4a0SFrançois Tigeot 43719edbd4a0SFrançois Tigeot if (vic) 43729edbd4a0SFrançois Tigeot frame->vic = vic; 43739edbd4a0SFrançois Tigeot else 43749edbd4a0SFrançois Tigeot frame->s3d_struct = s3d_structure_from_display_mode(mode); 43759edbd4a0SFrançois Tigeot 43769edbd4a0SFrançois Tigeot return 0; 43779edbd4a0SFrançois Tigeot } 43789edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); 43798621f407SFrançois Tigeot 43808621f407SFrançois Tigeot static int drm_parse_tiled_block(struct drm_connector *connector, 43818621f407SFrançois Tigeot struct displayid_block *block) 43828621f407SFrançois Tigeot { 43838621f407SFrançois Tigeot struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; 43848621f407SFrançois Tigeot u16 w, h; 43858621f407SFrançois Tigeot u8 tile_v_loc, tile_h_loc; 43868621f407SFrançois Tigeot u8 num_v_tile, num_h_tile; 43878621f407SFrançois Tigeot struct drm_tile_group *tg; 43888621f407SFrançois Tigeot 43898621f407SFrançois Tigeot w = tile->tile_size[0] | tile->tile_size[1] << 8; 43908621f407SFrançois Tigeot h = tile->tile_size[2] | tile->tile_size[3] << 8; 43918621f407SFrançois Tigeot 43928621f407SFrançois Tigeot num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); 43938621f407SFrançois Tigeot num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); 43948621f407SFrançois Tigeot tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); 43958621f407SFrançois Tigeot tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); 43968621f407SFrançois Tigeot 43978621f407SFrançois Tigeot connector->has_tile = true; 43988621f407SFrançois Tigeot if (tile->tile_cap & 0x80) 43998621f407SFrançois Tigeot connector->tile_is_single_monitor = true; 44008621f407SFrançois Tigeot 44018621f407SFrançois Tigeot connector->num_h_tile = num_h_tile + 1; 44028621f407SFrançois Tigeot connector->num_v_tile = num_v_tile + 1; 44038621f407SFrançois Tigeot connector->tile_h_loc = tile_h_loc; 44048621f407SFrançois Tigeot connector->tile_v_loc = tile_v_loc; 44058621f407SFrançois Tigeot connector->tile_h_size = w + 1; 44068621f407SFrançois Tigeot connector->tile_v_size = h + 1; 44078621f407SFrançois Tigeot 44088621f407SFrançois Tigeot DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); 44098621f407SFrançois Tigeot DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); 44108621f407SFrançois Tigeot DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", 44118621f407SFrançois Tigeot num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); 44128621f407SFrançois Tigeot DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); 44138621f407SFrançois Tigeot 44148621f407SFrançois Tigeot tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); 44158621f407SFrançois Tigeot if (!tg) { 44168621f407SFrançois Tigeot tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); 44178621f407SFrançois Tigeot } 44188621f407SFrançois Tigeot if (!tg) 44198621f407SFrançois Tigeot return -ENOMEM; 44208621f407SFrançois Tigeot 44218621f407SFrançois Tigeot if (connector->tile_group != tg) { 44228621f407SFrançois Tigeot /* if we haven't got a pointer, 44238621f407SFrançois Tigeot take the reference, drop ref to old tile group */ 44248621f407SFrançois Tigeot if (connector->tile_group) { 44258621f407SFrançois Tigeot drm_mode_put_tile_group(connector->dev, connector->tile_group); 44268621f407SFrançois Tigeot } 44278621f407SFrançois Tigeot connector->tile_group = tg; 44288621f407SFrançois Tigeot } else 44298621f407SFrançois Tigeot /* if same tile group, then release the ref we just took. */ 44308621f407SFrançois Tigeot drm_mode_put_tile_group(connector->dev, tg); 44318621f407SFrançois Tigeot return 0; 44328621f407SFrançois Tigeot } 44338621f407SFrançois Tigeot 44348621f407SFrançois Tigeot static int drm_parse_display_id(struct drm_connector *connector, 44358621f407SFrançois Tigeot u8 *displayid, int length, 44368621f407SFrançois Tigeot bool is_edid_extension) 44378621f407SFrançois Tigeot { 44388621f407SFrançois Tigeot /* if this is an EDID extension the first byte will be 0x70 */ 44398621f407SFrançois Tigeot int idx = 0; 44408621f407SFrançois Tigeot struct displayid_block *block; 44418621f407SFrançois Tigeot int ret; 44428621f407SFrançois Tigeot 44438621f407SFrançois Tigeot if (is_edid_extension) 44448621f407SFrançois Tigeot idx = 1; 44458621f407SFrançois Tigeot 44468621f407SFrançois Tigeot ret = validate_displayid(displayid, length, idx); 44478621f407SFrançois Tigeot if (ret) 44488621f407SFrançois Tigeot return ret; 44498621f407SFrançois Tigeot 44508621f407SFrançois Tigeot idx += sizeof(struct displayid_hdr); 44518621f407SFrançois Tigeot while (block = (struct displayid_block *)&displayid[idx], 44528621f407SFrançois Tigeot idx + sizeof(struct displayid_block) <= length && 44538621f407SFrançois Tigeot idx + sizeof(struct displayid_block) + block->num_bytes <= length && 44548621f407SFrançois Tigeot block->num_bytes > 0) { 44558621f407SFrançois Tigeot idx += block->num_bytes + sizeof(struct displayid_block); 44568621f407SFrançois Tigeot DRM_DEBUG_KMS("block id 0x%x, rev %d, len %d\n", 44578621f407SFrançois Tigeot block->tag, block->rev, block->num_bytes); 44588621f407SFrançois Tigeot 44598621f407SFrançois Tigeot switch (block->tag) { 44608621f407SFrançois Tigeot case DATA_BLOCK_TILED_DISPLAY: 44618621f407SFrançois Tigeot ret = drm_parse_tiled_block(connector, block); 44628621f407SFrançois Tigeot if (ret) 44638621f407SFrançois Tigeot return ret; 44648621f407SFrançois Tigeot break; 44658621f407SFrançois Tigeot case DATA_BLOCK_TYPE_1_DETAILED_TIMING: 44668621f407SFrançois Tigeot /* handled in mode gathering code. */ 44678621f407SFrançois Tigeot break; 44688621f407SFrançois Tigeot default: 44698621f407SFrançois Tigeot DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); 44708621f407SFrançois Tigeot break; 44718621f407SFrançois Tigeot } 44728621f407SFrançois Tigeot } 44738621f407SFrançois Tigeot return 0; 44748621f407SFrançois Tigeot } 44758621f407SFrançois Tigeot 44768621f407SFrançois Tigeot static void drm_get_displayid(struct drm_connector *connector, 44778621f407SFrançois Tigeot struct edid *edid) 44788621f407SFrançois Tigeot { 44798621f407SFrançois Tigeot void *displayid = NULL; 44808621f407SFrançois Tigeot int ret; 44818621f407SFrançois Tigeot connector->has_tile = false; 44828621f407SFrançois Tigeot displayid = drm_find_displayid_extension(edid); 44838621f407SFrançois Tigeot if (!displayid) { 44848621f407SFrançois Tigeot /* drop reference to any tile group we had */ 44858621f407SFrançois Tigeot goto out_drop_ref; 44868621f407SFrançois Tigeot } 44878621f407SFrançois Tigeot 44888621f407SFrançois Tigeot ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true); 44898621f407SFrançois Tigeot if (ret < 0) 44908621f407SFrançois Tigeot goto out_drop_ref; 44918621f407SFrançois Tigeot if (!connector->has_tile) 44928621f407SFrançois Tigeot goto out_drop_ref; 44938621f407SFrançois Tigeot return; 44948621f407SFrançois Tigeot out_drop_ref: 44958621f407SFrançois Tigeot if (connector->tile_group) { 44968621f407SFrançois Tigeot drm_mode_put_tile_group(connector->dev, connector->tile_group); 44978621f407SFrançois Tigeot connector->tile_group = NULL; 44988621f407SFrançois Tigeot } 44998621f407SFrançois Tigeot return; 45008621f407SFrançois Tigeot } 4501