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> 3418e26a6dSFrançois Tigeot #include <drm/drmP.h> 3518e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 369edbd4a0SFrançois Tigeot #include <linux/string.h> 37d82bf20eSFrançois Tigeot 385718399fSFrançois Tigeot #include <bus/iicbus/iic.h> 395718399fSFrançois Tigeot #include <bus/iicbus/iiconf.h> 405718399fSFrançois Tigeot #include "iicbus_if.h" 415718399fSFrançois Tigeot 425718399fSFrançois Tigeot #define version_greater(edid, maj, min) \ 435718399fSFrançois Tigeot (((edid)->version > (maj)) || \ 445718399fSFrançois Tigeot ((edid)->version == (maj) && (edid)->revision > (min))) 455718399fSFrançois Tigeot 465718399fSFrançois Tigeot #define EDID_EST_TIMINGS 16 475718399fSFrançois Tigeot #define EDID_STD_TIMINGS 8 485718399fSFrançois Tigeot #define EDID_DETAILED_TIMINGS 4 495718399fSFrançois Tigeot 505718399fSFrançois Tigeot /* 515718399fSFrançois Tigeot * EDID blocks out in the wild have a variety of bugs, try to collect 525718399fSFrançois Tigeot * them here (note that userspace may work around broken monitors first, 535718399fSFrançois Tigeot * but fixes should make their way here so that the kernel "just works" 545718399fSFrançois Tigeot * on as many displays as possible). 555718399fSFrançois Tigeot */ 565718399fSFrançois Tigeot 575718399fSFrançois Tigeot /* First detailed mode wrong, use largest 60Hz mode */ 585718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) 595718399fSFrançois Tigeot /* Reported 135MHz pixel clock is too high, needs adjustment */ 605718399fSFrançois Tigeot #define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) 615718399fSFrançois Tigeot /* Prefer the largest mode at 75 Hz */ 625718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) 635718399fSFrançois Tigeot /* Detail timing is in cm not mm */ 645718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_IN_CM (1 << 3) 655718399fSFrançois Tigeot /* Detailed timing descriptors have bogus size values, so just take the 665718399fSFrançois Tigeot * maximum size and use that. 675718399fSFrançois Tigeot */ 685718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) 695718399fSFrançois Tigeot /* Monitor forgot to set the first detailed is preferred bit. */ 705718399fSFrançois Tigeot #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) 715718399fSFrançois Tigeot /* use +hsync +vsync for detailed mode */ 725718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) 736e29dde8SFrançois Tigeot /* Force reduced-blanking timings for detailed modes */ 746e29dde8SFrançois Tigeot #define EDID_QUIRK_FORCE_REDUCED_BLANKING (1 << 7) 759edbd4a0SFrançois Tigeot /* Force 8bpc */ 769edbd4a0SFrançois Tigeot #define EDID_QUIRK_FORCE_8BPC (1 << 8) 77ba55f2f5SFrançois Tigeot /* Force 12bpc */ 78ba55f2f5SFrançois Tigeot #define EDID_QUIRK_FORCE_12BPC (1 << 9) 795718399fSFrançois Tigeot 805718399fSFrançois Tigeot struct detailed_mode_closure { 815718399fSFrançois Tigeot struct drm_connector *connector; 825718399fSFrançois Tigeot struct edid *edid; 835718399fSFrançois Tigeot bool preferred; 845718399fSFrançois Tigeot u32 quirks; 855718399fSFrançois Tigeot int modes; 865718399fSFrançois Tigeot }; 875718399fSFrançois Tigeot 885718399fSFrançois Tigeot #define LEVEL_DMT 0 895718399fSFrançois Tigeot #define LEVEL_GTF 1 905718399fSFrançois Tigeot #define LEVEL_GTF2 2 915718399fSFrançois Tigeot #define LEVEL_CVT 3 925718399fSFrançois Tigeot 935718399fSFrançois Tigeot static struct edid_quirk { 946e29dde8SFrançois Tigeot char vendor[4]; 955718399fSFrançois Tigeot int product_id; 965718399fSFrançois Tigeot u32 quirks; 975718399fSFrançois Tigeot } edid_quirk_list[] = { 985718399fSFrançois Tigeot /* Acer AL1706 */ 995718399fSFrançois Tigeot { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, 1005718399fSFrançois Tigeot /* Acer F51 */ 1015718399fSFrançois Tigeot { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, 1025718399fSFrançois Tigeot /* Unknown Acer */ 1035718399fSFrançois Tigeot { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1045718399fSFrançois Tigeot 1055718399fSFrançois Tigeot /* Belinea 10 15 55 */ 1065718399fSFrançois Tigeot { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, 1075718399fSFrançois Tigeot { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, 1085718399fSFrançois Tigeot 1095718399fSFrançois Tigeot /* Envision Peripherals, Inc. EN-7100e */ 1105718399fSFrançois Tigeot { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, 1115718399fSFrançois Tigeot /* Envision EN2028 */ 1125718399fSFrançois Tigeot { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 }, 1135718399fSFrançois Tigeot 1145718399fSFrançois Tigeot /* Funai Electronics PM36B */ 1155718399fSFrançois Tigeot { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | 1165718399fSFrançois Tigeot EDID_QUIRK_DETAILED_IN_CM }, 1175718399fSFrançois Tigeot 1185718399fSFrançois Tigeot /* LG Philips LCD LP154W01-A5 */ 1195718399fSFrançois Tigeot { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1205718399fSFrançois Tigeot { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1215718399fSFrançois Tigeot 1225718399fSFrançois Tigeot /* Philips 107p5 CRT */ 1235718399fSFrançois Tigeot { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1245718399fSFrançois Tigeot 1255718399fSFrançois Tigeot /* Proview AY765C */ 1265718399fSFrançois Tigeot { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1275718399fSFrançois Tigeot 1285718399fSFrançois Tigeot /* Samsung SyncMaster 205BW. Note: irony */ 1295718399fSFrançois Tigeot { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, 1305718399fSFrançois Tigeot /* Samsung SyncMaster 22[5-6]BW */ 1315718399fSFrançois Tigeot { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, 1325718399fSFrançois Tigeot { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, 1336e29dde8SFrançois Tigeot 134ba55f2f5SFrançois Tigeot /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */ 135ba55f2f5SFrançois Tigeot { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC }, 136ba55f2f5SFrançois Tigeot 1376e29dde8SFrançois Tigeot /* ViewSonic VA2026w */ 1386e29dde8SFrançois Tigeot { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, 1399edbd4a0SFrançois Tigeot 1409edbd4a0SFrançois Tigeot /* Medion MD 30217 PG */ 1419edbd4a0SFrançois Tigeot { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, 1429edbd4a0SFrançois Tigeot 1439edbd4a0SFrançois Tigeot /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ 1449edbd4a0SFrançois Tigeot { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, 1455718399fSFrançois Tigeot }; 1465718399fSFrançois Tigeot 147d82bf20eSFrançois Tigeot /* 148d82bf20eSFrançois Tigeot * Autogenerated from the DMT spec. 149d82bf20eSFrançois Tigeot * This table is copied from xfree86/modes/xf86EdidModes.c. 150d82bf20eSFrançois Tigeot */ 151d82bf20eSFrançois Tigeot static const struct drm_display_mode drm_dmt_modes[] = { 152d82bf20eSFrançois Tigeot /* 640x350@85Hz */ 153d82bf20eSFrançois Tigeot { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 154d82bf20eSFrançois Tigeot 736, 832, 0, 350, 382, 385, 445, 0, 155d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 156d82bf20eSFrançois Tigeot /* 640x400@85Hz */ 157d82bf20eSFrançois Tigeot { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 158d82bf20eSFrançois Tigeot 736, 832, 0, 400, 401, 404, 445, 0, 159d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 160d82bf20eSFrançois Tigeot /* 720x400@85Hz */ 161d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, 162d82bf20eSFrançois Tigeot 828, 936, 0, 400, 401, 404, 446, 0, 163d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 164d82bf20eSFrançois Tigeot /* 640x480@60Hz */ 165d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 166d82bf20eSFrançois Tigeot 752, 800, 0, 480, 489, 492, 525, 0, 167d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 168d82bf20eSFrançois Tigeot /* 640x480@72Hz */ 169d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 170d82bf20eSFrançois Tigeot 704, 832, 0, 480, 489, 492, 520, 0, 171d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 172d82bf20eSFrançois Tigeot /* 640x480@75Hz */ 173d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 174d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 175d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 176d82bf20eSFrançois Tigeot /* 640x480@85Hz */ 177d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, 178d82bf20eSFrançois Tigeot 752, 832, 0, 480, 481, 484, 509, 0, 179d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 180d82bf20eSFrançois Tigeot /* 800x600@56Hz */ 181d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 182d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 183d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 184d82bf20eSFrançois Tigeot /* 800x600@60Hz */ 185d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 186d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 187d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 188d82bf20eSFrançois Tigeot /* 800x600@72Hz */ 189d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 190d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 191d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 192d82bf20eSFrançois Tigeot /* 800x600@75Hz */ 193d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 194d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 195d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 196d82bf20eSFrançois Tigeot /* 800x600@85Hz */ 197d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, 198d82bf20eSFrançois Tigeot 896, 1048, 0, 600, 601, 604, 631, 0, 199d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 200d82bf20eSFrançois Tigeot /* 800x600@120Hz RB */ 201d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848, 202d82bf20eSFrançois Tigeot 880, 960, 0, 600, 603, 607, 636, 0, 203d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 204d82bf20eSFrançois Tigeot /* 848x480@60Hz */ 205d82bf20eSFrançois Tigeot { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, 206d82bf20eSFrançois Tigeot 976, 1088, 0, 480, 486, 494, 517, 0, 207d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 208d82bf20eSFrançois Tigeot /* 1024x768@43Hz, interlace */ 209d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, 210d82bf20eSFrançois Tigeot 1208, 1264, 0, 768, 768, 772, 817, 0, 211d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 212d82bf20eSFrançois Tigeot DRM_MODE_FLAG_INTERLACE) }, 213d82bf20eSFrançois Tigeot /* 1024x768@60Hz */ 214d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 215d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 216d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 217d82bf20eSFrançois Tigeot /* 1024x768@70Hz */ 218d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 219d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 220d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 221d82bf20eSFrançois Tigeot /* 1024x768@75Hz */ 222d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, 223d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 224d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 225d82bf20eSFrançois Tigeot /* 1024x768@85Hz */ 226d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, 227d82bf20eSFrançois Tigeot 1168, 1376, 0, 768, 769, 772, 808, 0, 228d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 229d82bf20eSFrançois Tigeot /* 1024x768@120Hz RB */ 230d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072, 231d82bf20eSFrançois Tigeot 1104, 1184, 0, 768, 771, 775, 813, 0, 232d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 233d82bf20eSFrançois Tigeot /* 1152x864@75Hz */ 234d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 235d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 236d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 237d82bf20eSFrançois Tigeot /* 1280x768@60Hz RB */ 238d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328, 239d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 790, 0, 240d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 241d82bf20eSFrançois Tigeot /* 1280x768@60Hz */ 242d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, 243d82bf20eSFrançois Tigeot 1472, 1664, 0, 768, 771, 778, 798, 0, 244d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 245d82bf20eSFrançois Tigeot /* 1280x768@75Hz */ 246d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, 247d82bf20eSFrançois Tigeot 1488, 1696, 0, 768, 771, 778, 805, 0, 248d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 249d82bf20eSFrançois Tigeot /* 1280x768@85Hz */ 250d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, 251d82bf20eSFrançois Tigeot 1496, 1712, 0, 768, 771, 778, 809, 0, 252d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 253d82bf20eSFrançois Tigeot /* 1280x768@120Hz RB */ 254d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328, 255d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 813, 0, 256d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 257d82bf20eSFrançois Tigeot /* 1280x800@60Hz RB */ 258d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328, 259d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 823, 0, 260d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 261d82bf20eSFrançois Tigeot /* 1280x800@60Hz */ 262d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, 263d82bf20eSFrançois Tigeot 1480, 1680, 0, 800, 803, 809, 831, 0, 264d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 265d82bf20eSFrançois Tigeot /* 1280x800@75Hz */ 266d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, 267d82bf20eSFrançois Tigeot 1488, 1696, 0, 800, 803, 809, 838, 0, 268d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 269d82bf20eSFrançois Tigeot /* 1280x800@85Hz */ 270d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, 271d82bf20eSFrançois Tigeot 1496, 1712, 0, 800, 803, 809, 843, 0, 272d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 273d82bf20eSFrançois Tigeot /* 1280x800@120Hz RB */ 274d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328, 275d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 847, 0, 276d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 277d82bf20eSFrançois Tigeot /* 1280x960@60Hz */ 278d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, 279d82bf20eSFrançois Tigeot 1488, 1800, 0, 960, 961, 964, 1000, 0, 280d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 281d82bf20eSFrançois Tigeot /* 1280x960@85Hz */ 282d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, 283d82bf20eSFrançois Tigeot 1504, 1728, 0, 960, 961, 964, 1011, 0, 284d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 285d82bf20eSFrançois Tigeot /* 1280x960@120Hz RB */ 286d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328, 287d82bf20eSFrançois Tigeot 1360, 1440, 0, 960, 963, 967, 1017, 0, 288d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 289d82bf20eSFrançois Tigeot /* 1280x1024@60Hz */ 290d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, 291d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 292d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 293d82bf20eSFrançois Tigeot /* 1280x1024@75Hz */ 294d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 295d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 296d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 297d82bf20eSFrançois Tigeot /* 1280x1024@85Hz */ 298d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, 299d82bf20eSFrançois Tigeot 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, 300d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 301d82bf20eSFrançois Tigeot /* 1280x1024@120Hz RB */ 302d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328, 303d82bf20eSFrançois Tigeot 1360, 1440, 0, 1024, 1027, 1034, 1084, 0, 304d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 305d82bf20eSFrançois Tigeot /* 1360x768@60Hz */ 306d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, 307d82bf20eSFrançois Tigeot 1536, 1792, 0, 768, 771, 777, 795, 0, 308d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 309d82bf20eSFrançois Tigeot /* 1360x768@120Hz RB */ 310d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408, 311d82bf20eSFrançois Tigeot 1440, 1520, 0, 768, 771, 776, 813, 0, 312d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 313d82bf20eSFrançois Tigeot /* 1400x1050@60Hz RB */ 314d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448, 315d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1080, 0, 316d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 317d82bf20eSFrançois Tigeot /* 1400x1050@60Hz */ 318d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, 319d82bf20eSFrançois Tigeot 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 320d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 321d82bf20eSFrançois Tigeot /* 1400x1050@75Hz */ 322d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, 323d82bf20eSFrançois Tigeot 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, 324d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 325d82bf20eSFrançois Tigeot /* 1400x1050@85Hz */ 326d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, 327d82bf20eSFrançois Tigeot 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, 328d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 329d82bf20eSFrançois Tigeot /* 1400x1050@120Hz RB */ 330d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448, 331d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1112, 0, 332d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 333d82bf20eSFrançois Tigeot /* 1440x900@60Hz RB */ 334d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, 335d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 926, 0, 336d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 337d82bf20eSFrançois Tigeot /* 1440x900@60Hz */ 338d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, 339d82bf20eSFrançois Tigeot 1672, 1904, 0, 900, 903, 909, 934, 0, 340d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 341d82bf20eSFrançois Tigeot /* 1440x900@75Hz */ 342d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, 343d82bf20eSFrançois Tigeot 1688, 1936, 0, 900, 903, 909, 942, 0, 344d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 345d82bf20eSFrançois Tigeot /* 1440x900@85Hz */ 346d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, 347d82bf20eSFrançois Tigeot 1696, 1952, 0, 900, 903, 909, 948, 0, 348d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 349d82bf20eSFrançois Tigeot /* 1440x900@120Hz RB */ 350d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488, 351d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 953, 0, 352d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 353d82bf20eSFrançois Tigeot /* 1600x1200@60Hz */ 354d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, 355d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 356d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 357d82bf20eSFrançois Tigeot /* 1600x1200@65Hz */ 358d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, 359d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 360d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 361d82bf20eSFrançois Tigeot /* 1600x1200@70Hz */ 362d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, 363d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 364d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 365d82bf20eSFrançois Tigeot /* 1600x1200@75Hz */ 366d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, 367d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 368d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 369d82bf20eSFrançois Tigeot /* 1600x1200@85Hz */ 370d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, 371d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 372d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 373d82bf20eSFrançois Tigeot /* 1600x1200@120Hz RB */ 374d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648, 375d82bf20eSFrançois Tigeot 1680, 1760, 0, 1200, 1203, 1207, 1271, 0, 376d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 377d82bf20eSFrançois Tigeot /* 1680x1050@60Hz RB */ 378d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728, 379d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1080, 0, 380d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 381d82bf20eSFrançois Tigeot /* 1680x1050@60Hz */ 382d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, 383d82bf20eSFrançois Tigeot 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 384d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 385d82bf20eSFrançois Tigeot /* 1680x1050@75Hz */ 386d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, 387d82bf20eSFrançois Tigeot 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, 388d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 389d82bf20eSFrançois Tigeot /* 1680x1050@85Hz */ 390d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, 391d82bf20eSFrançois Tigeot 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, 392d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 393d82bf20eSFrançois Tigeot /* 1680x1050@120Hz RB */ 394d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728, 395d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1112, 0, 396d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 397d82bf20eSFrançois Tigeot /* 1792x1344@60Hz */ 398d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, 399d82bf20eSFrançois Tigeot 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, 400d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 401d82bf20eSFrançois Tigeot /* 1792x1344@75Hz */ 402d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, 403d82bf20eSFrançois Tigeot 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, 404d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 405d82bf20eSFrançois Tigeot /* 1792x1344@120Hz RB */ 406d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840, 407d82bf20eSFrançois Tigeot 1872, 1952, 0, 1344, 1347, 1351, 1423, 0, 408d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 409d82bf20eSFrançois Tigeot /* 1856x1392@60Hz */ 410d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, 411d82bf20eSFrançois Tigeot 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, 412d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 413d82bf20eSFrançois Tigeot /* 1856x1392@75Hz */ 414d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, 415d82bf20eSFrançois Tigeot 2208, 2560, 0, 1392, 1395, 1399, 1500, 0, 416d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 417d82bf20eSFrançois Tigeot /* 1856x1392@120Hz RB */ 418d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904, 419d82bf20eSFrançois Tigeot 1936, 2016, 0, 1392, 1395, 1399, 1474, 0, 420d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 421d82bf20eSFrançois Tigeot /* 1920x1200@60Hz RB */ 422d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968, 423d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1235, 0, 424d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 425d82bf20eSFrançois Tigeot /* 1920x1200@60Hz */ 426d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, 427d82bf20eSFrançois Tigeot 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 428d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 429d82bf20eSFrançois Tigeot /* 1920x1200@75Hz */ 430d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, 431d82bf20eSFrançois Tigeot 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, 432d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 433d82bf20eSFrançois Tigeot /* 1920x1200@85Hz */ 434d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, 435d82bf20eSFrançois Tigeot 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, 436d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 437d82bf20eSFrançois Tigeot /* 1920x1200@120Hz RB */ 438d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968, 439d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1271, 0, 440d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 441d82bf20eSFrançois Tigeot /* 1920x1440@60Hz */ 442d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, 443d82bf20eSFrançois Tigeot 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, 444d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 445d82bf20eSFrançois Tigeot /* 1920x1440@75Hz */ 446d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, 447d82bf20eSFrançois Tigeot 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, 448d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 449d82bf20eSFrançois Tigeot /* 1920x1440@120Hz RB */ 450d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968, 451d82bf20eSFrançois Tigeot 2000, 2080, 0, 1440, 1443, 1447, 1525, 0, 452d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 453d82bf20eSFrançois Tigeot /* 2560x1600@60Hz RB */ 454d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608, 455d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1646, 0, 456d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 457d82bf20eSFrançois Tigeot /* 2560x1600@60Hz */ 458d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, 459d82bf20eSFrançois Tigeot 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 460d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 461d82bf20eSFrançois Tigeot /* 2560x1600@75HZ */ 462d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, 463d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, 464d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 465d82bf20eSFrançois Tigeot /* 2560x1600@85HZ */ 466d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, 467d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, 468d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 469d82bf20eSFrançois Tigeot /* 2560x1600@120Hz RB */ 470d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608, 471d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1694, 0, 472d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 473d82bf20eSFrançois Tigeot }; 474d82bf20eSFrançois Tigeot 4759edbd4a0SFrançois Tigeot /* 4769edbd4a0SFrançois Tigeot * These more or less come from the DMT spec. The 720x400 modes are 4779edbd4a0SFrançois Tigeot * inferred from historical 80x25 practice. The 640x480@67 and 832x624@75 4789edbd4a0SFrançois Tigeot * modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode 4799edbd4a0SFrançois Tigeot * should be 1152x870, again for the Mac, but instead we use the x864 DMT 4809edbd4a0SFrançois Tigeot * mode. 4819edbd4a0SFrançois Tigeot * 4829edbd4a0SFrançois Tigeot * The DMT modes have been fact-checked; the rest are mild guesses. 4839edbd4a0SFrançois Tigeot */ 484d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_est_modes[] = { 485d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 486d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 487d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ 488d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 489d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 490d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ 491d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 492d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 493d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ 494d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 495d82bf20eSFrançois Tigeot 704, 832, 0, 480, 489, 491, 520, 0, 496d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ 497d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, 498d82bf20eSFrançois Tigeot 768, 864, 0, 480, 483, 486, 525, 0, 499d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ 500d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, 501d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 502d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ 503d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, 504d82bf20eSFrançois Tigeot 846, 900, 0, 400, 421, 423, 449, 0, 505d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ 506d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, 507d82bf20eSFrançois Tigeot 846, 900, 0, 400, 412, 414, 449, 0, 508d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ 509d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 510d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 511d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ 512d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, 513d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 514d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ 515d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 516d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 517d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ 518d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 519d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 520d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ 521d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, 522d82bf20eSFrançois Tigeot 1208, 1264, 0, 768, 768, 776, 817, 0, 523d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ 524d82bf20eSFrançois Tigeot { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, 525d82bf20eSFrançois Tigeot 928, 1152, 0, 624, 625, 628, 667, 0, 526d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ 527d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 528d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 529d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ 530d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 531d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 532d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ 533d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 534d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 535d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ 536d82bf20eSFrançois Tigeot }; 537d82bf20eSFrançois Tigeot 538d82bf20eSFrançois Tigeot struct minimode { 539d82bf20eSFrançois Tigeot short w; 540d82bf20eSFrançois Tigeot short h; 541d82bf20eSFrançois Tigeot short r; 542d82bf20eSFrançois Tigeot short rb; 543d82bf20eSFrançois Tigeot }; 544d82bf20eSFrançois Tigeot 545d82bf20eSFrançois Tigeot static const struct minimode est3_modes[] = { 546d82bf20eSFrançois Tigeot /* byte 6 */ 547d82bf20eSFrançois Tigeot { 640, 350, 85, 0 }, 548d82bf20eSFrançois Tigeot { 640, 400, 85, 0 }, 549d82bf20eSFrançois Tigeot { 720, 400, 85, 0 }, 550d82bf20eSFrançois Tigeot { 640, 480, 85, 0 }, 551d82bf20eSFrançois Tigeot { 848, 480, 60, 0 }, 552d82bf20eSFrançois Tigeot { 800, 600, 85, 0 }, 553d82bf20eSFrançois Tigeot { 1024, 768, 85, 0 }, 554d82bf20eSFrançois Tigeot { 1152, 864, 75, 0 }, 555d82bf20eSFrançois Tigeot /* byte 7 */ 556d82bf20eSFrançois Tigeot { 1280, 768, 60, 1 }, 557d82bf20eSFrançois Tigeot { 1280, 768, 60, 0 }, 558d82bf20eSFrançois Tigeot { 1280, 768, 75, 0 }, 559d82bf20eSFrançois Tigeot { 1280, 768, 85, 0 }, 560d82bf20eSFrançois Tigeot { 1280, 960, 60, 0 }, 561d82bf20eSFrançois Tigeot { 1280, 960, 85, 0 }, 562d82bf20eSFrançois Tigeot { 1280, 1024, 60, 0 }, 563d82bf20eSFrançois Tigeot { 1280, 1024, 85, 0 }, 564d82bf20eSFrançois Tigeot /* byte 8 */ 565d82bf20eSFrançois Tigeot { 1360, 768, 60, 0 }, 566d82bf20eSFrançois Tigeot { 1440, 900, 60, 1 }, 567d82bf20eSFrançois Tigeot { 1440, 900, 60, 0 }, 568d82bf20eSFrançois Tigeot { 1440, 900, 75, 0 }, 569d82bf20eSFrançois Tigeot { 1440, 900, 85, 0 }, 570d82bf20eSFrançois Tigeot { 1400, 1050, 60, 1 }, 571d82bf20eSFrançois Tigeot { 1400, 1050, 60, 0 }, 572d82bf20eSFrançois Tigeot { 1400, 1050, 75, 0 }, 573d82bf20eSFrançois Tigeot /* byte 9 */ 574d82bf20eSFrançois Tigeot { 1400, 1050, 85, 0 }, 575d82bf20eSFrançois Tigeot { 1680, 1050, 60, 1 }, 576d82bf20eSFrançois Tigeot { 1680, 1050, 60, 0 }, 577d82bf20eSFrançois Tigeot { 1680, 1050, 75, 0 }, 578d82bf20eSFrançois Tigeot { 1680, 1050, 85, 0 }, 579d82bf20eSFrançois Tigeot { 1600, 1200, 60, 0 }, 580d82bf20eSFrançois Tigeot { 1600, 1200, 65, 0 }, 581d82bf20eSFrançois Tigeot { 1600, 1200, 70, 0 }, 582d82bf20eSFrançois Tigeot /* byte 10 */ 583d82bf20eSFrançois Tigeot { 1600, 1200, 75, 0 }, 584d82bf20eSFrançois Tigeot { 1600, 1200, 85, 0 }, 585d82bf20eSFrançois Tigeot { 1792, 1344, 60, 0 }, 5869edbd4a0SFrançois Tigeot { 1792, 1344, 75, 0 }, 587d82bf20eSFrançois Tigeot { 1856, 1392, 60, 0 }, 588d82bf20eSFrançois Tigeot { 1856, 1392, 75, 0 }, 589d82bf20eSFrançois Tigeot { 1920, 1200, 60, 1 }, 590d82bf20eSFrançois Tigeot { 1920, 1200, 60, 0 }, 591d82bf20eSFrançois Tigeot /* byte 11 */ 592d82bf20eSFrançois Tigeot { 1920, 1200, 75, 0 }, 593d82bf20eSFrançois Tigeot { 1920, 1200, 85, 0 }, 594d82bf20eSFrançois Tigeot { 1920, 1440, 60, 0 }, 595d82bf20eSFrançois Tigeot { 1920, 1440, 75, 0 }, 596d82bf20eSFrançois Tigeot }; 597d82bf20eSFrançois Tigeot 598d82bf20eSFrançois Tigeot static const struct minimode extra_modes[] = { 599d82bf20eSFrançois Tigeot { 1024, 576, 60, 0 }, 600d82bf20eSFrançois Tigeot { 1366, 768, 60, 0 }, 601d82bf20eSFrançois Tigeot { 1600, 900, 60, 0 }, 602d82bf20eSFrançois Tigeot { 1680, 945, 60, 0 }, 603d82bf20eSFrançois Tigeot { 1920, 1080, 60, 0 }, 604d82bf20eSFrançois Tigeot { 2048, 1152, 60, 0 }, 605d82bf20eSFrançois Tigeot { 2048, 1536, 60, 0 }, 606d82bf20eSFrançois Tigeot }; 607d82bf20eSFrançois Tigeot 608d82bf20eSFrançois Tigeot /* 609d82bf20eSFrançois Tigeot * Probably taken from CEA-861 spec. 610d82bf20eSFrançois Tigeot * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. 611d82bf20eSFrançois Tigeot */ 612d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_cea_modes[] = { 613d82bf20eSFrançois Tigeot /* 1 - 640x480@60Hz */ 614d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 615d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 6169edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6179edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 618d82bf20eSFrançois Tigeot /* 2 - 720x480@60Hz */ 619d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 620d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 6219edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6229edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 623d82bf20eSFrançois Tigeot /* 3 - 720x480@60Hz */ 624d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 625d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 6269edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6279edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 628d82bf20eSFrançois Tigeot /* 4 - 1280x720@60Hz */ 629d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 630d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 6319edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 6329edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 633d82bf20eSFrançois Tigeot /* 5 - 1920x1080i@60Hz */ 634d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 635d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 636d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 6379edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 6389edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 639d82bf20eSFrançois Tigeot /* 6 - 1440x480i@60Hz */ 640d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 641d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 642d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6439edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 6449edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 645d82bf20eSFrançois Tigeot /* 7 - 1440x480i@60Hz */ 646d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 647d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 648d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6499edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 6509edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 651d82bf20eSFrançois Tigeot /* 8 - 1440x240@60Hz */ 652d82bf20eSFrançois Tigeot { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 653d82bf20eSFrançois Tigeot 1602, 1716, 0, 240, 244, 247, 262, 0, 654d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6559edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 6569edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 657d82bf20eSFrançois Tigeot /* 9 - 1440x240@60Hz */ 658d82bf20eSFrançois Tigeot { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 659d82bf20eSFrançois Tigeot 1602, 1716, 0, 240, 244, 247, 262, 0, 660d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6619edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 6629edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 663d82bf20eSFrançois Tigeot /* 10 - 2880x480i@60Hz */ 664d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 665d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 666d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6679edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 6689edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 669d82bf20eSFrançois Tigeot /* 11 - 2880x480i@60Hz */ 670d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 671d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 672d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 6739edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 6749edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 675d82bf20eSFrançois Tigeot /* 12 - 2880x240@60Hz */ 676d82bf20eSFrançois Tigeot { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 677d82bf20eSFrançois Tigeot 3204, 3432, 0, 240, 244, 247, 262, 0, 6789edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6799edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 680d82bf20eSFrançois Tigeot /* 13 - 2880x240@60Hz */ 681d82bf20eSFrançois Tigeot { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 682d82bf20eSFrançois Tigeot 3204, 3432, 0, 240, 244, 247, 262, 0, 6839edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6849edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 685d82bf20eSFrançois Tigeot /* 14 - 1440x480@60Hz */ 686d82bf20eSFrançois Tigeot { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, 687d82bf20eSFrançois Tigeot 1596, 1716, 0, 480, 489, 495, 525, 0, 6889edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6899edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 690d82bf20eSFrançois Tigeot /* 15 - 1440x480@60Hz */ 691d82bf20eSFrançois Tigeot { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, 692d82bf20eSFrançois Tigeot 1596, 1716, 0, 480, 489, 495, 525, 0, 6939edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 6949edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 695d82bf20eSFrançois Tigeot /* 16 - 1920x1080@60Hz */ 696d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 697d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 6989edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 6999edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 700d82bf20eSFrançois Tigeot /* 17 - 720x576@50Hz */ 701d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 702d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 7039edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7049edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 705d82bf20eSFrançois Tigeot /* 18 - 720x576@50Hz */ 706d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 707d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 7089edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7099edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 710d82bf20eSFrançois Tigeot /* 19 - 1280x720@50Hz */ 711d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, 712d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 7139edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7149edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 715d82bf20eSFrançois Tigeot /* 20 - 1920x1080i@50Hz */ 716d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 717d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 718d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 7199edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7209edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 721d82bf20eSFrançois Tigeot /* 21 - 1440x576i@50Hz */ 722d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 723d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 724d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7259edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 7269edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 727d82bf20eSFrançois Tigeot /* 22 - 1440x576i@50Hz */ 728d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 729d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 730d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7319edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 7329edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 733d82bf20eSFrançois Tigeot /* 23 - 1440x288@50Hz */ 734d82bf20eSFrançois Tigeot { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 735d82bf20eSFrançois Tigeot 1590, 1728, 0, 288, 290, 293, 312, 0, 736d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7379edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 7389edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 739d82bf20eSFrançois Tigeot /* 24 - 1440x288@50Hz */ 740d82bf20eSFrançois Tigeot { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 741d82bf20eSFrançois Tigeot 1590, 1728, 0, 288, 290, 293, 312, 0, 742d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7439edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 7449edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 745d82bf20eSFrançois Tigeot /* 25 - 2880x576i@50Hz */ 746d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 747d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 748d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7499edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7509edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 751d82bf20eSFrançois Tigeot /* 26 - 2880x576i@50Hz */ 752d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 753d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 754d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 7559edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 7569edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 757d82bf20eSFrançois Tigeot /* 27 - 2880x288@50Hz */ 758d82bf20eSFrançois Tigeot { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 759d82bf20eSFrançois Tigeot 3180, 3456, 0, 288, 290, 293, 312, 0, 7609edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7619edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 762d82bf20eSFrançois Tigeot /* 28 - 2880x288@50Hz */ 763d82bf20eSFrançois Tigeot { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 764d82bf20eSFrançois Tigeot 3180, 3456, 0, 288, 290, 293, 312, 0, 7659edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7669edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 767d82bf20eSFrançois Tigeot /* 29 - 1440x576@50Hz */ 768d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 769d82bf20eSFrançois Tigeot 1592, 1728, 0, 576, 581, 586, 625, 0, 7709edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7719edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 772d82bf20eSFrançois Tigeot /* 30 - 1440x576@50Hz */ 773d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 774d82bf20eSFrançois Tigeot 1592, 1728, 0, 576, 581, 586, 625, 0, 7759edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 7769edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 777d82bf20eSFrançois Tigeot /* 31 - 1920x1080@50Hz */ 778d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 779d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 7809edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7819edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 782d82bf20eSFrançois Tigeot /* 32 - 1920x1080@24Hz */ 783d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, 784d82bf20eSFrançois Tigeot 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, 7859edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7869edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 787d82bf20eSFrançois Tigeot /* 33 - 1920x1080@25Hz */ 788d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 789d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 7909edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7919edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 792d82bf20eSFrançois Tigeot /* 34 - 1920x1080@30Hz */ 793d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 794d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 7959edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 7969edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 797d82bf20eSFrançois Tigeot /* 35 - 2880x480@60Hz */ 798d82bf20eSFrançois Tigeot { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, 799d82bf20eSFrançois Tigeot 3192, 3432, 0, 480, 489, 495, 525, 0, 8009edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8019edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 802d82bf20eSFrançois Tigeot /* 36 - 2880x480@60Hz */ 803d82bf20eSFrançois Tigeot { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, 804d82bf20eSFrançois Tigeot 3192, 3432, 0, 480, 489, 495, 525, 0, 8059edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8069edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 807d82bf20eSFrançois Tigeot /* 37 - 2880x576@50Hz */ 808d82bf20eSFrançois Tigeot { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, 809d82bf20eSFrançois Tigeot 3184, 3456, 0, 576, 581, 586, 625, 0, 8109edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8119edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 812d82bf20eSFrançois Tigeot /* 38 - 2880x576@50Hz */ 813d82bf20eSFrançois Tigeot { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, 814d82bf20eSFrançois Tigeot 3184, 3456, 0, 576, 581, 586, 625, 0, 8159edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8169edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 817d82bf20eSFrançois Tigeot /* 39 - 1920x1080i@50Hz */ 818d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, 819d82bf20eSFrançois Tigeot 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, 820d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | 8219edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 8229edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 823d82bf20eSFrançois Tigeot /* 40 - 1920x1080i@100Hz */ 824d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 825d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 826d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 8279edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 8289edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 829d82bf20eSFrançois Tigeot /* 41 - 1280x720@100Hz */ 830d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720, 831d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 8329edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8339edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 834d82bf20eSFrançois Tigeot /* 42 - 720x576@100Hz */ 835d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 836d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8379edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8389edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 839d82bf20eSFrançois Tigeot /* 43 - 720x576@100Hz */ 840d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 841d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8429edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8439edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 844d82bf20eSFrançois Tigeot /* 44 - 1440x576i@100Hz */ 845d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 846d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 847d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8489edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 8499edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 850d82bf20eSFrançois Tigeot /* 45 - 1440x576i@100Hz */ 851d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 852d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 853d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8549edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 8559edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 856d82bf20eSFrançois Tigeot /* 46 - 1920x1080i@120Hz */ 857d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 858d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 859d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 8609edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 8619edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 862d82bf20eSFrançois Tigeot /* 47 - 1280x720@120Hz */ 863d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390, 864d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 8659edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 8669edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 867d82bf20eSFrançois Tigeot /* 48 - 720x480@120Hz */ 868d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 869d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 8709edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8719edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 872d82bf20eSFrançois Tigeot /* 49 - 720x480@120Hz */ 873d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 874d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 8759edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8769edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 877d82bf20eSFrançois Tigeot /* 50 - 1440x480i@120Hz */ 878d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 879d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 880d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8819edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 8829edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 883d82bf20eSFrançois Tigeot /* 51 - 1440x480i@120Hz */ 884d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 885d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 886d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 8879edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 8889edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 889d82bf20eSFrançois Tigeot /* 52 - 720x576@200Hz */ 890d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 891d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8929edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8939edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 894d82bf20eSFrançois Tigeot /* 53 - 720x576@200Hz */ 895d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 896d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 8979edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 8989edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 899d82bf20eSFrançois Tigeot /* 54 - 1440x576i@200Hz */ 900d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 901d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 902d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9039edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9049edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 905d82bf20eSFrançois Tigeot /* 55 - 1440x576i@200Hz */ 906d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 907d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 908d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9099edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9109edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 911d82bf20eSFrançois Tigeot /* 56 - 720x480@240Hz */ 912d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 913d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9149edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9159edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 916d82bf20eSFrançois Tigeot /* 57 - 720x480@240Hz */ 917d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 918d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 9199edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 9209edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 921d82bf20eSFrançois Tigeot /* 58 - 1440x480i@240 */ 922d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 923d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 924d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9259edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9269edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 927d82bf20eSFrançois Tigeot /* 59 - 1440x480i@240 */ 928d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 929d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 930d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 9319edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 9329edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 933d82bf20eSFrançois Tigeot /* 60 - 1280x720@24Hz */ 934d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, 935d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 9369edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9379edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 938d82bf20eSFrançois Tigeot /* 61 - 1280x720@25Hz */ 939d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700, 940d82bf20eSFrançois Tigeot 3740, 3960, 0, 720, 725, 730, 750, 0, 9419edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9429edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 943d82bf20eSFrançois Tigeot /* 62 - 1280x720@30Hz */ 944d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040, 945d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 9469edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9479edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 948d82bf20eSFrançois Tigeot /* 63 - 1920x1080@120Hz */ 949d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, 950d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 9519edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9529edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 953d82bf20eSFrançois Tigeot /* 64 - 1920x1080@100Hz */ 954d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, 955d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 9569edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9579edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 9589edbd4a0SFrançois Tigeot }; 9599edbd4a0SFrançois Tigeot 9609edbd4a0SFrançois Tigeot /* 9619edbd4a0SFrançois Tigeot * HDMI 1.4 4k modes. 9629edbd4a0SFrançois Tigeot */ 9639edbd4a0SFrançois Tigeot static const struct drm_display_mode edid_4k_modes[] = { 9649edbd4a0SFrançois Tigeot /* 1 - 3840x2160@30Hz */ 9659edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 9669edbd4a0SFrançois Tigeot 3840, 4016, 4104, 4400, 0, 9679edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 9689edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9699edbd4a0SFrançois Tigeot .vrefresh = 30, }, 9709edbd4a0SFrançois Tigeot /* 2 - 3840x2160@25Hz */ 9719edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 9729edbd4a0SFrançois Tigeot 3840, 4896, 4984, 5280, 0, 9739edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 9749edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9759edbd4a0SFrançois Tigeot .vrefresh = 25, }, 9769edbd4a0SFrançois Tigeot /* 3 - 3840x2160@24Hz */ 9779edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 9789edbd4a0SFrançois Tigeot 3840, 5116, 5204, 5500, 0, 9799edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 9809edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9819edbd4a0SFrançois Tigeot .vrefresh = 24, }, 9829edbd4a0SFrançois Tigeot /* 4 - 4096x2160@24Hz (SMPTE) */ 9839edbd4a0SFrançois Tigeot { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 9849edbd4a0SFrançois Tigeot 4096, 5116, 5204, 5500, 0, 9859edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 9869edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 9879edbd4a0SFrançois Tigeot .vrefresh = 24, }, 988d82bf20eSFrançois Tigeot }; 989d82bf20eSFrançois Tigeot 9905718399fSFrançois Tigeot /*** DDC fetch and block validation ***/ 9915718399fSFrançois Tigeot 9925718399fSFrançois Tigeot static const u8 edid_header[] = { 9935718399fSFrançois Tigeot 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 9945718399fSFrançois Tigeot }; 9955718399fSFrançois Tigeot 996ba55f2f5SFrançois Tigeot /** 997ba55f2f5SFrançois Tigeot * drm_edid_header_is_valid - sanity check the header of the base EDID block 998ba55f2f5SFrançois Tigeot * @raw_edid: pointer to raw base EDID block 999ba55f2f5SFrançois Tigeot * 1000ba55f2f5SFrançois Tigeot * Sanity check the header of the base EDID block. 1001ba55f2f5SFrançois Tigeot * 1002ba55f2f5SFrançois Tigeot * Return: 8 if the header is perfect, down to 0 if it's totally wrong. 10035718399fSFrançois Tigeot */ 10045718399fSFrançois Tigeot int drm_edid_header_is_valid(const u8 *raw_edid) 10055718399fSFrançois Tigeot { 10065718399fSFrançois Tigeot int i, score = 0; 10075718399fSFrançois Tigeot 10085718399fSFrançois Tigeot for (i = 0; i < sizeof(edid_header); i++) 10095718399fSFrançois Tigeot if (raw_edid[i] == edid_header[i]) 10105718399fSFrançois Tigeot score++; 10115718399fSFrançois Tigeot 10125718399fSFrançois Tigeot return score; 10135718399fSFrançois Tigeot } 1014ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_header_is_valid); 1015ce3d36d7SFrançois Tigeot 1016ce3d36d7SFrançois Tigeot static int edid_fixup __read_mostly = 6; 1017d82bf20eSFrançois Tigeot module_param_named(edid_fixup, edid_fixup, int, 0400); 1018d82bf20eSFrançois Tigeot MODULE_PARM_DESC(edid_fixup, 1019d82bf20eSFrançois Tigeot "Minimum number of valid EDID header bytes (0-8, default 6)"); 10205718399fSFrançois Tigeot 1021ba55f2f5SFrançois Tigeot /** 1022ba55f2f5SFrançois Tigeot * drm_edid_block_valid - Sanity check the EDID block (base or extension) 1023ba55f2f5SFrançois Tigeot * @raw_edid: pointer to raw EDID block 1024ba55f2f5SFrançois Tigeot * @block: type of block to validate (0 for base, extension otherwise) 1025ba55f2f5SFrançois Tigeot * @print_bad_edid: if true, dump bad EDID blocks to the console 1026ba55f2f5SFrançois Tigeot * 1027ba55f2f5SFrançois Tigeot * Validate a base or extension EDID block and optionally dump bad blocks to 1028ba55f2f5SFrançois Tigeot * the console. 1029ba55f2f5SFrançois Tigeot * 1030ba55f2f5SFrançois Tigeot * Return: True if the block is valid, false otherwise. 10315718399fSFrançois Tigeot */ 1032ce3d36d7SFrançois Tigeot bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) 10335718399fSFrançois Tigeot { 10345718399fSFrançois Tigeot int i; 10355718399fSFrançois Tigeot u8 csum = 0; 10365718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 10375718399fSFrançois Tigeot 10389edbd4a0SFrançois Tigeot if (WARN_ON(!raw_edid)) 10399edbd4a0SFrançois Tigeot return false; 10409edbd4a0SFrançois Tigeot 1041ce3d36d7SFrançois Tigeot if (edid_fixup > 8 || edid_fixup < 0) 1042ce3d36d7SFrançois Tigeot edid_fixup = 6; 1043ce3d36d7SFrançois Tigeot 1044ce3d36d7SFrançois Tigeot if (block == 0) { 10455718399fSFrançois Tigeot int score = drm_edid_header_is_valid(raw_edid); 10465718399fSFrançois Tigeot if (score == 8) ; 1047ce3d36d7SFrançois Tigeot else if (score >= edid_fixup) { 10485718399fSFrançois Tigeot DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); 10495718399fSFrançois Tigeot memcpy(raw_edid, edid_header, sizeof(edid_header)); 10505718399fSFrançois Tigeot } else { 10515718399fSFrançois Tigeot goto bad; 10525718399fSFrançois Tigeot } 10535718399fSFrançois Tigeot } 10545718399fSFrançois Tigeot 10555718399fSFrançois Tigeot for (i = 0; i < EDID_LENGTH; i++) 10565718399fSFrançois Tigeot csum += raw_edid[i]; 10575718399fSFrançois Tigeot if (csum) { 1058ce3d36d7SFrançois Tigeot if (print_bad_edid) { 10595718399fSFrançois Tigeot DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); 1060ce3d36d7SFrançois Tigeot } 10615718399fSFrançois Tigeot 10625718399fSFrançois Tigeot /* allow CEA to slide through, switches mangle this */ 10635718399fSFrançois Tigeot if (raw_edid[0] != 0x02) 10645718399fSFrançois Tigeot goto bad; 10655718399fSFrançois Tigeot } 10665718399fSFrançois Tigeot 10675718399fSFrançois Tigeot /* per-block-type checks */ 10685718399fSFrançois Tigeot switch (raw_edid[0]) { 10695718399fSFrançois Tigeot case 0: /* base */ 10705718399fSFrançois Tigeot if (edid->version != 1) { 10715718399fSFrançois Tigeot DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); 10725718399fSFrançois Tigeot goto bad; 10735718399fSFrançois Tigeot } 10745718399fSFrançois Tigeot 10755718399fSFrançois Tigeot if (edid->revision > 4) 10765718399fSFrançois Tigeot DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); 10775718399fSFrançois Tigeot break; 10785718399fSFrançois Tigeot 10795718399fSFrançois Tigeot default: 10805718399fSFrançois Tigeot break; 10815718399fSFrançois Tigeot } 10825718399fSFrançois Tigeot 10839edbd4a0SFrançois Tigeot return true; 10845718399fSFrançois Tigeot 10855718399fSFrançois Tigeot bad: 10869edbd4a0SFrançois Tigeot if (print_bad_edid) { 10879edbd4a0SFrançois Tigeot printk(KERN_ERR "Raw EDID:\n"); 10885718399fSFrançois Tigeot for (i = 0; i < EDID_LENGTH; ) { 10895718399fSFrançois Tigeot kprintf("%02x", raw_edid[i]); 10905718399fSFrançois Tigeot i++; 10915718399fSFrançois Tigeot if (i % 16 == 0 || i == EDID_LENGTH) 10925718399fSFrançois Tigeot kprintf("\n"); 10935718399fSFrançois Tigeot else if (i % 8 == 0) 10945718399fSFrançois Tigeot kprintf(" "); 10955718399fSFrançois Tigeot else 10965718399fSFrançois Tigeot kprintf(" "); 10975718399fSFrançois Tigeot } 10985718399fSFrançois Tigeot } 10999edbd4a0SFrançois Tigeot return false; 11005718399fSFrançois Tigeot } 1101ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_block_valid); 11025718399fSFrançois Tigeot 11035718399fSFrançois Tigeot /** 11045718399fSFrançois Tigeot * drm_edid_is_valid - sanity check EDID data 11055718399fSFrançois Tigeot * @edid: EDID data 11065718399fSFrançois Tigeot * 11075718399fSFrançois Tigeot * Sanity-check an entire EDID record (including extensions) 1108ba55f2f5SFrançois Tigeot * 1109ba55f2f5SFrançois Tigeot * Return: True if the EDID data is valid, false otherwise. 11105718399fSFrançois Tigeot */ 11115718399fSFrançois Tigeot bool drm_edid_is_valid(struct edid *edid) 11125718399fSFrançois Tigeot { 11135718399fSFrançois Tigeot int i; 11145718399fSFrançois Tigeot u8 *raw = (u8 *)edid; 11155718399fSFrançois Tigeot 11165718399fSFrançois Tigeot if (!edid) 11175718399fSFrançois Tigeot return false; 11185718399fSFrançois Tigeot 11195718399fSFrançois Tigeot for (i = 0; i <= edid->extensions; i++) 1120ce3d36d7SFrançois Tigeot if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true)) 11215718399fSFrançois Tigeot return false; 11225718399fSFrançois Tigeot 11235718399fSFrançois Tigeot return true; 11245718399fSFrançois Tigeot } 1125ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_is_valid); 11265718399fSFrançois Tigeot 11275718399fSFrançois Tigeot #define DDC_SEGMENT_ADDR 0x30 11285718399fSFrançois Tigeot /** 1129ba55f2f5SFrançois Tigeot * drm_do_probe_ddc_edid() - get EDID information via I2C 1130ba55f2f5SFrançois Tigeot * @adapter: I2C device adaptor 1131ba55f2f5SFrançois Tigeot * @buf: EDID data buffer to be filled 1132ba55f2f5SFrançois Tigeot * @block: 128 byte EDID block to start fetching from 1133ba55f2f5SFrançois Tigeot * @len: EDID data buffer length to fetch 11345718399fSFrançois Tigeot * 1135ba55f2f5SFrançois Tigeot * Try to fetch EDID information by calling I2C driver functions. 11365718399fSFrançois Tigeot * 1137ba55f2f5SFrançois Tigeot * Return: 0 on success or -1 on failure. 11385718399fSFrançois Tigeot */ 11395718399fSFrançois Tigeot static int 1140d82bf20eSFrançois Tigeot drm_do_probe_ddc_edid(struct device *adapter, unsigned char *buf, 11415718399fSFrançois Tigeot int block, int len) 11425718399fSFrançois Tigeot { 11435718399fSFrançois Tigeot unsigned char start = block * EDID_LENGTH; 11446f486c69SFrançois Tigeot unsigned char segment = block >> 1; 11456f486c69SFrançois Tigeot unsigned char xfers = segment ? 3 : 2; 11465718399fSFrançois Tigeot int ret, retries = 5; 11475718399fSFrançois Tigeot 1148ba55f2f5SFrançois Tigeot /* 1149ba55f2f5SFrançois Tigeot * The core I2C driver will automatically retry the transfer if the 11505718399fSFrançois Tigeot * adapter reports EAGAIN. However, we find that bit-banging transfers 11515718399fSFrançois Tigeot * are susceptible to errors under a heavily loaded machine and 11525718399fSFrançois Tigeot * generate spurious NAKs and timeouts. Retrying the transfer 11535718399fSFrançois Tigeot * of the individual block a few times seems to overcome this. 11545718399fSFrançois Tigeot */ 11555718399fSFrançois Tigeot do { 11566e29dde8SFrançois Tigeot struct i2c_msg msgs[] = { 11575718399fSFrançois Tigeot { 11586f486c69SFrançois Tigeot .slave = DDC_SEGMENT_ADDR << 1, 11596f486c69SFrançois Tigeot .flags = 0, 11606f486c69SFrançois Tigeot .len = 1, 11616f486c69SFrançois Tigeot .buf = &segment, 11626f486c69SFrançois Tigeot }, { 11635718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 11646e29dde8SFrançois Tigeot .flags = 0, 11655718399fSFrançois Tigeot .len = 1, 11665718399fSFrançois Tigeot .buf = &start, 11675718399fSFrançois Tigeot }, { 11685718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 11696e29dde8SFrançois Tigeot .flags = I2C_M_RD, 11705718399fSFrançois Tigeot .len = len, 11715718399fSFrançois Tigeot .buf = buf, 11725718399fSFrançois Tigeot } 11735718399fSFrançois Tigeot }; 11746f486c69SFrançois Tigeot 11756f486c69SFrançois Tigeot /* 1176ba55f2f5SFrançois Tigeot * Avoid sending the segment addr to not upset non-compliant 1177ba55f2f5SFrançois Tigeot * DDC monitors. 11786f486c69SFrançois Tigeot */ 11796f486c69SFrançois Tigeot ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers); 11806f486c69SFrançois Tigeot 11815718399fSFrançois Tigeot if (ret != 0) 11825718399fSFrançois Tigeot DRM_DEBUG_KMS("iicbus_transfer countdown %d error %d\n", 11835718399fSFrançois Tigeot retries, ret); 11845718399fSFrançois Tigeot } while (ret != 0 && --retries); 11855718399fSFrançois Tigeot 11865718399fSFrançois Tigeot return (ret == 0 ? 0 : -1); 11875718399fSFrançois Tigeot } 11885718399fSFrançois Tigeot 11895718399fSFrançois Tigeot static bool drm_edid_is_zero(u8 *in_edid, int length) 11905718399fSFrançois Tigeot { 11915718399fSFrançois Tigeot int i; 11925718399fSFrançois Tigeot u32 *raw_edid = (u32 *)in_edid; 11935718399fSFrançois Tigeot 11945718399fSFrançois Tigeot for (i = 0; i < length / 4; i++) 11955718399fSFrançois Tigeot if (*(raw_edid + i) != 0) 11965718399fSFrançois Tigeot return false; 1197d82bf20eSFrançois Tigeot 11985718399fSFrançois Tigeot return true; 11995718399fSFrançois Tigeot } 12005718399fSFrançois Tigeot 12015718399fSFrançois Tigeot static u8 * 1202d82bf20eSFrançois Tigeot drm_do_get_edid(struct drm_connector *connector, struct device *adapter) 12035718399fSFrançois Tigeot { 12045718399fSFrançois Tigeot int i, j = 0, valid_extensions = 0; 12055718399fSFrançois Tigeot u8 *block, *new; 1206ce3d36d7SFrançois Tigeot bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); 12075718399fSFrançois Tigeot 12089edbd4a0SFrançois Tigeot if ((block = kmalloc(EDID_LENGTH, M_DRM, M_WAITOK)) == NULL) 12099edbd4a0SFrançois Tigeot return NULL; 12105718399fSFrançois Tigeot 12115718399fSFrançois Tigeot /* base block fetch */ 12125718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 12135718399fSFrançois Tigeot if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) 12145718399fSFrançois Tigeot goto out; 1215ce3d36d7SFrançois Tigeot if (drm_edid_block_valid(block, 0, print_bad_edid)) 12165718399fSFrançois Tigeot break; 12175718399fSFrançois Tigeot if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { 12185718399fSFrançois Tigeot connector->null_edid_counter++; 12195718399fSFrançois Tigeot goto carp; 12205718399fSFrançois Tigeot } 12215718399fSFrançois Tigeot } 12225718399fSFrançois Tigeot if (i == 4) 12235718399fSFrançois Tigeot goto carp; 12245718399fSFrançois Tigeot 12255718399fSFrançois Tigeot /* if there's no extensions, we're done */ 12265718399fSFrançois Tigeot if (block[0x7e] == 0) 12275718399fSFrançois Tigeot return block; 12285718399fSFrançois Tigeot 1229d82bf20eSFrançois Tigeot new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 1230ce3d36d7SFrançois Tigeot if (!new) 1231ce3d36d7SFrançois Tigeot goto out; 12325718399fSFrançois Tigeot block = new; 12335718399fSFrançois Tigeot 12345718399fSFrançois Tigeot for (j = 1; j <= block[0x7e]; j++) { 12355718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 12365718399fSFrançois Tigeot if (drm_do_probe_ddc_edid(adapter, 12375718399fSFrançois Tigeot block + (valid_extensions + 1) * EDID_LENGTH, 12385718399fSFrançois Tigeot j, EDID_LENGTH)) 12395718399fSFrançois Tigeot goto out; 1240ce3d36d7SFrançois Tigeot if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) { 12415718399fSFrançois Tigeot valid_extensions++; 12425718399fSFrançois Tigeot break; 12435718399fSFrançois Tigeot } 12445718399fSFrançois Tigeot } 1245d82bf20eSFrançois Tigeot 1246d82bf20eSFrançois Tigeot if (i == 4 && print_bad_edid) { 1247ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, 1248ce3d36d7SFrançois Tigeot "%s: Ignoring invalid EDID block %d.\n", 1249ba55f2f5SFrançois Tigeot connector->name, j); 1250d82bf20eSFrançois Tigeot 1251d82bf20eSFrançois Tigeot connector->bad_edid_counter++; 1252d82bf20eSFrançois Tigeot } 12535718399fSFrançois Tigeot } 12545718399fSFrançois Tigeot 12555718399fSFrançois Tigeot if (valid_extensions != block[0x7e]) { 12565718399fSFrançois Tigeot block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; 12575718399fSFrançois Tigeot block[0x7e] = valid_extensions; 1258ba55f2f5SFrançois Tigeot new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 1259ce3d36d7SFrançois Tigeot if (!new) 1260ce3d36d7SFrançois Tigeot goto out; 12615718399fSFrançois Tigeot block = new; 12625718399fSFrançois Tigeot } 12635718399fSFrançois Tigeot 12645718399fSFrançois Tigeot return block; 12655718399fSFrançois Tigeot 12665718399fSFrançois Tigeot carp: 1267ce3d36d7SFrançois Tigeot if (print_bad_edid) { 1268ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", 1269ba55f2f5SFrançois Tigeot connector->name, j); 1270ce3d36d7SFrançois Tigeot } 1271ce3d36d7SFrançois Tigeot connector->bad_edid_counter++; 12725718399fSFrançois Tigeot 12735718399fSFrançois Tigeot out: 1274158486a6SFrançois Tigeot kfree(block); 12755718399fSFrançois Tigeot return NULL; 12765718399fSFrançois Tigeot } 12775718399fSFrançois Tigeot 12785718399fSFrançois Tigeot /** 1279ba55f2f5SFrançois Tigeot * drm_probe_ddc() - probe DDC presence 1280ba55f2f5SFrançois Tigeot * @adapter: I2C adapter to probe 12815718399fSFrançois Tigeot * 1282ba55f2f5SFrançois Tigeot * Return: True on success, false on failure. 12835718399fSFrançois Tigeot */ 1284ce3d36d7SFrançois Tigeot bool 1285d82bf20eSFrançois Tigeot drm_probe_ddc(struct device *adapter) 12865718399fSFrançois Tigeot { 12875718399fSFrançois Tigeot unsigned char out; 12885718399fSFrançois Tigeot 12895718399fSFrançois Tigeot return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0); 12905718399fSFrançois Tigeot } 1291ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_probe_ddc); 12925718399fSFrançois Tigeot 12935718399fSFrançois Tigeot /** 12945718399fSFrançois Tigeot * drm_get_edid - get EDID data, if available 12955718399fSFrançois Tigeot * @connector: connector we're probing 1296ba55f2f5SFrançois Tigeot * @adapter: I2C adapter to use for DDC 12975718399fSFrançois Tigeot * 1298ba55f2f5SFrançois Tigeot * Poke the given I2C channel to grab EDID data if possible. If found, 12995718399fSFrançois Tigeot * attach it to the connector. 13005718399fSFrançois Tigeot * 1301ba55f2f5SFrançois Tigeot * Return: Pointer to valid EDID or NULL if we couldn't find any. 13025718399fSFrançois Tigeot */ 13035718399fSFrançois Tigeot struct edid *drm_get_edid(struct drm_connector *connector, 1304d82bf20eSFrançois Tigeot struct device *adapter) 13055718399fSFrançois Tigeot { 13065718399fSFrançois Tigeot struct edid *edid = NULL; 13075718399fSFrançois Tigeot 13085718399fSFrançois Tigeot if (drm_probe_ddc(adapter)) 13095718399fSFrançois Tigeot edid = (struct edid *)drm_do_get_edid(connector, adapter); 13105718399fSFrançois Tigeot 13115718399fSFrançois Tigeot return edid; 13125718399fSFrançois Tigeot } 1313af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid); 13145718399fSFrançois Tigeot 13159edbd4a0SFrançois Tigeot /** 13169edbd4a0SFrançois Tigeot * drm_edid_duplicate - duplicate an EDID and the extensions 13179edbd4a0SFrançois Tigeot * @edid: EDID to duplicate 13189edbd4a0SFrançois Tigeot * 1319ba55f2f5SFrançois Tigeot * Return: Pointer to duplicated EDID or NULL on allocation failure. 13209edbd4a0SFrançois Tigeot */ 13219edbd4a0SFrançois Tigeot struct edid *drm_edid_duplicate(const struct edid *edid) 13229edbd4a0SFrançois Tigeot { 13239edbd4a0SFrançois Tigeot return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); 13249edbd4a0SFrançois Tigeot } 13259edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_duplicate); 13269edbd4a0SFrançois Tigeot 13275718399fSFrançois Tigeot /*** EDID parsing ***/ 13285718399fSFrançois Tigeot 13295718399fSFrançois Tigeot /** 13305718399fSFrançois Tigeot * edid_vendor - match a string against EDID's obfuscated vendor field 13315718399fSFrançois Tigeot * @edid: EDID to match 13325718399fSFrançois Tigeot * @vendor: vendor string 13335718399fSFrançois Tigeot * 13345718399fSFrançois Tigeot * Returns true if @vendor is in @edid, false otherwise 13355718399fSFrançois Tigeot */ 13365718399fSFrançois Tigeot static bool edid_vendor(struct edid *edid, char *vendor) 13375718399fSFrançois Tigeot { 13385718399fSFrançois Tigeot char edid_vendor[3]; 13395718399fSFrançois Tigeot 13405718399fSFrançois Tigeot edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; 13415718399fSFrançois Tigeot edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | 13425718399fSFrançois Tigeot ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; 13435718399fSFrançois Tigeot edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; 13445718399fSFrançois Tigeot 13455718399fSFrançois Tigeot return !strncmp(edid_vendor, vendor, 3); 13465718399fSFrançois Tigeot } 13475718399fSFrançois Tigeot 13485718399fSFrançois Tigeot /** 13495718399fSFrançois Tigeot * edid_get_quirks - return quirk flags for a given EDID 13505718399fSFrançois Tigeot * @edid: EDID to process 13515718399fSFrançois Tigeot * 13525718399fSFrançois Tigeot * This tells subsequent routines what fixes they need to apply. 13535718399fSFrançois Tigeot */ 13545718399fSFrançois Tigeot static u32 edid_get_quirks(struct edid *edid) 13555718399fSFrançois Tigeot { 13565718399fSFrançois Tigeot struct edid_quirk *quirk; 13575718399fSFrançois Tigeot int i; 13585718399fSFrançois Tigeot 13596e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { 13605718399fSFrançois Tigeot quirk = &edid_quirk_list[i]; 13615718399fSFrançois Tigeot 13625718399fSFrançois Tigeot if (edid_vendor(edid, quirk->vendor) && 13635718399fSFrançois Tigeot (EDID_PRODUCT_ID(edid) == quirk->product_id)) 13645718399fSFrançois Tigeot return quirk->quirks; 13655718399fSFrançois Tigeot } 13665718399fSFrançois Tigeot 13675718399fSFrançois Tigeot return 0; 13685718399fSFrançois Tigeot } 13695718399fSFrançois Tigeot 13705718399fSFrançois Tigeot #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) 13719edbd4a0SFrançois Tigeot #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t))) 13725718399fSFrançois Tigeot 13735718399fSFrançois Tigeot /** 13745718399fSFrançois Tigeot * edid_fixup_preferred - set preferred modes based on quirk list 13755718399fSFrançois Tigeot * @connector: has mode list to fix up 13765718399fSFrançois Tigeot * @quirks: quirks list 13775718399fSFrançois Tigeot * 13785718399fSFrançois Tigeot * Walk the mode list for @connector, clearing the preferred status 13795718399fSFrançois Tigeot * on existing modes and setting it anew for the right mode ala @quirks. 13805718399fSFrançois Tigeot */ 13815718399fSFrançois Tigeot static void edid_fixup_preferred(struct drm_connector *connector, 13825718399fSFrançois Tigeot u32 quirks) 13835718399fSFrançois Tigeot { 13845718399fSFrançois Tigeot struct drm_display_mode *t, *cur_mode, *preferred_mode; 13855718399fSFrançois Tigeot int target_refresh = 0; 13869edbd4a0SFrançois Tigeot int cur_vrefresh, preferred_vrefresh; 13875718399fSFrançois Tigeot 13885718399fSFrançois Tigeot if (list_empty(&connector->probed_modes)) 13895718399fSFrançois Tigeot return; 13905718399fSFrançois Tigeot 13915718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_60) 13925718399fSFrançois Tigeot target_refresh = 60; 13935718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_75) 13945718399fSFrançois Tigeot target_refresh = 75; 13955718399fSFrançois Tigeot 13965718399fSFrançois Tigeot preferred_mode = list_first_entry(&connector->probed_modes, 13975718399fSFrançois Tigeot struct drm_display_mode, head); 13985718399fSFrançois Tigeot 13995718399fSFrançois Tigeot list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { 14005718399fSFrançois Tigeot cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; 14015718399fSFrançois Tigeot 14025718399fSFrançois Tigeot if (cur_mode == preferred_mode) 14035718399fSFrançois Tigeot continue; 14045718399fSFrançois Tigeot 14055718399fSFrançois Tigeot /* Largest mode is preferred */ 14065718399fSFrançois Tigeot if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) 14075718399fSFrançois Tigeot preferred_mode = cur_mode; 14085718399fSFrançois Tigeot 14099edbd4a0SFrançois Tigeot cur_vrefresh = cur_mode->vrefresh ? 14109edbd4a0SFrançois Tigeot cur_mode->vrefresh : drm_mode_vrefresh(cur_mode); 14119edbd4a0SFrançois Tigeot preferred_vrefresh = preferred_mode->vrefresh ? 14129edbd4a0SFrançois Tigeot preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode); 14135718399fSFrançois Tigeot /* At a given size, try to get closest to target refresh */ 14145718399fSFrançois Tigeot if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && 14159edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) < 14169edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) { 14175718399fSFrançois Tigeot preferred_mode = cur_mode; 14185718399fSFrançois Tigeot } 14195718399fSFrançois Tigeot } 14205718399fSFrançois Tigeot 14215718399fSFrançois Tigeot preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; 14225718399fSFrançois Tigeot } 14235718399fSFrançois Tigeot 1424ce3d36d7SFrançois Tigeot static bool 1425ce3d36d7SFrançois Tigeot mode_is_rb(const struct drm_display_mode *mode) 1426ce3d36d7SFrançois Tigeot { 1427ce3d36d7SFrançois Tigeot return (mode->htotal - mode->hdisplay == 160) && 1428ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hdisplay == 80) && 1429ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hsync_start == 32) && 1430ce3d36d7SFrançois Tigeot (mode->vsync_start - mode->vdisplay == 3); 1431ce3d36d7SFrançois Tigeot } 1432ce3d36d7SFrançois Tigeot 1433af4b81b9SFrançois Tigeot /* 1434af4b81b9SFrançois Tigeot * drm_mode_find_dmt - Create a copy of a mode if present in DMT 1435af4b81b9SFrançois Tigeot * @dev: Device to duplicate against 1436af4b81b9SFrançois Tigeot * @hsize: Mode width 1437af4b81b9SFrançois Tigeot * @vsize: Mode height 1438af4b81b9SFrançois Tigeot * @fresh: Mode refresh rate 1439af4b81b9SFrançois Tigeot * @rb: Mode reduced-blanking-ness 1440af4b81b9SFrançois Tigeot * 1441af4b81b9SFrançois Tigeot * Walk the DMT mode list looking for a match for the given parameters. 1442ba55f2f5SFrançois Tigeot * 1443ba55f2f5SFrançois Tigeot * Return: A newly allocated copy of the mode, or NULL if not found. 1444af4b81b9SFrançois Tigeot */ 14455718399fSFrançois Tigeot struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, 1446ce3d36d7SFrançois Tigeot int hsize, int vsize, int fresh, 1447ce3d36d7SFrançois Tigeot bool rb) 14485718399fSFrançois Tigeot { 14495718399fSFrançois Tigeot int i; 14505718399fSFrançois Tigeot 1451d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 1452ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 1453ce3d36d7SFrançois Tigeot if (hsize != ptr->hdisplay) 1454ce3d36d7SFrançois Tigeot continue; 1455ce3d36d7SFrançois Tigeot if (vsize != ptr->vdisplay) 1456ce3d36d7SFrançois Tigeot continue; 1457ce3d36d7SFrançois Tigeot if (fresh != drm_mode_vrefresh(ptr)) 1458ce3d36d7SFrançois Tigeot continue; 1459ce3d36d7SFrançois Tigeot if (rb != mode_is_rb(ptr)) 1460ce3d36d7SFrançois Tigeot continue; 1461ce3d36d7SFrançois Tigeot 1462ce3d36d7SFrançois Tigeot return drm_mode_duplicate(dev, ptr); 14635718399fSFrançois Tigeot } 1464ce3d36d7SFrançois Tigeot 1465ce3d36d7SFrançois Tigeot return NULL; 14665718399fSFrançois Tigeot } 1467af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_find_dmt); 14685718399fSFrançois Tigeot 14695718399fSFrançois Tigeot typedef void detailed_cb(struct detailed_timing *timing, void *closure); 14705718399fSFrançois Tigeot 14715718399fSFrançois Tigeot static void 14725718399fSFrançois Tigeot cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 14735718399fSFrançois Tigeot { 14745718399fSFrançois Tigeot int i, n = 0; 1475e3b244c9SFrançois Tigeot u8 d = ext[0x02]; 14765718399fSFrançois Tigeot u8 *det_base = ext + d; 14775718399fSFrançois Tigeot 1478e3b244c9SFrançois Tigeot n = (127 - d) / 18; 14795718399fSFrançois Tigeot for (i = 0; i < n; i++) 14805718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 14815718399fSFrançois Tigeot } 14825718399fSFrançois Tigeot 14835718399fSFrançois Tigeot static void 14845718399fSFrançois Tigeot vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 14855718399fSFrançois Tigeot { 14865718399fSFrançois Tigeot unsigned int i, n = min((int)ext[0x02], 6); 14875718399fSFrançois Tigeot u8 *det_base = ext + 5; 14885718399fSFrançois Tigeot 14895718399fSFrançois Tigeot if (ext[0x01] != 1) 14905718399fSFrançois Tigeot return; /* unknown version */ 14915718399fSFrançois Tigeot 14925718399fSFrançois Tigeot for (i = 0; i < n; i++) 14935718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 14945718399fSFrançois Tigeot } 14955718399fSFrançois Tigeot 14965718399fSFrançois Tigeot static void 14975718399fSFrançois Tigeot drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) 14985718399fSFrançois Tigeot { 14995718399fSFrançois Tigeot int i; 15005718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 15015718399fSFrançois Tigeot 15025718399fSFrançois Tigeot if (edid == NULL) 15035718399fSFrançois Tigeot return; 15045718399fSFrançois Tigeot 15055718399fSFrançois Tigeot for (i = 0; i < EDID_DETAILED_TIMINGS; i++) 15065718399fSFrançois Tigeot cb(&(edid->detailed_timings[i]), closure); 15075718399fSFrançois Tigeot 15085718399fSFrançois Tigeot for (i = 1; i <= raw_edid[0x7e]; i++) { 15095718399fSFrançois Tigeot u8 *ext = raw_edid + (i * EDID_LENGTH); 15105718399fSFrançois Tigeot switch (*ext) { 15115718399fSFrançois Tigeot case CEA_EXT: 15125718399fSFrançois Tigeot cea_for_each_detailed_block(ext, cb, closure); 15135718399fSFrançois Tigeot break; 15145718399fSFrançois Tigeot case VTB_EXT: 15155718399fSFrançois Tigeot vtb_for_each_detailed_block(ext, cb, closure); 15165718399fSFrançois Tigeot break; 15175718399fSFrançois Tigeot default: 15185718399fSFrançois Tigeot break; 15195718399fSFrançois Tigeot } 15205718399fSFrançois Tigeot } 15215718399fSFrançois Tigeot } 15225718399fSFrançois Tigeot 15235718399fSFrançois Tigeot static void 15245718399fSFrançois Tigeot is_rb(struct detailed_timing *t, void *data) 15255718399fSFrançois Tigeot { 15265718399fSFrançois Tigeot u8 *r = (u8 *)t; 15275718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE) 15285718399fSFrançois Tigeot if (r[15] & 0x10) 15295718399fSFrançois Tigeot *(bool *)data = true; 15305718399fSFrançois Tigeot } 15315718399fSFrançois Tigeot 15325718399fSFrançois Tigeot /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ 15335718399fSFrançois Tigeot static bool 15345718399fSFrançois Tigeot drm_monitor_supports_rb(struct edid *edid) 15355718399fSFrançois Tigeot { 15365718399fSFrançois Tigeot if (edid->revision >= 4) { 15376e29dde8SFrançois Tigeot bool ret = false; 15385718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); 15395718399fSFrançois Tigeot return ret; 15405718399fSFrançois Tigeot } 15415718399fSFrançois Tigeot 15425718399fSFrançois Tigeot return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); 15435718399fSFrançois Tigeot } 15445718399fSFrançois Tigeot 15455718399fSFrançois Tigeot static void 15465718399fSFrançois Tigeot find_gtf2(struct detailed_timing *t, void *data) 15475718399fSFrançois Tigeot { 15485718399fSFrançois Tigeot u8 *r = (u8 *)t; 15495718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02) 15505718399fSFrançois Tigeot *(u8 **)data = r; 15515718399fSFrançois Tigeot } 15525718399fSFrançois Tigeot 15535718399fSFrançois Tigeot /* Secondary GTF curve kicks in above some break frequency */ 15545718399fSFrançois Tigeot static int 15555718399fSFrançois Tigeot drm_gtf2_hbreak(struct edid *edid) 15565718399fSFrançois Tigeot { 15575718399fSFrançois Tigeot u8 *r = NULL; 15585718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15595718399fSFrançois Tigeot return r ? (r[12] * 2) : 0; 15605718399fSFrançois Tigeot } 15615718399fSFrançois Tigeot 15625718399fSFrançois Tigeot static int 15635718399fSFrançois Tigeot drm_gtf2_2c(struct edid *edid) 15645718399fSFrançois Tigeot { 15655718399fSFrançois Tigeot u8 *r = NULL; 15665718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15675718399fSFrançois Tigeot return r ? r[13] : 0; 15685718399fSFrançois Tigeot } 15695718399fSFrançois Tigeot 15705718399fSFrançois Tigeot static int 15715718399fSFrançois Tigeot drm_gtf2_m(struct edid *edid) 15725718399fSFrançois Tigeot { 15735718399fSFrançois Tigeot u8 *r = NULL; 15745718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15755718399fSFrançois Tigeot return r ? (r[15] << 8) + r[14] : 0; 15765718399fSFrançois Tigeot } 15775718399fSFrançois Tigeot 15785718399fSFrançois Tigeot static int 15795718399fSFrançois Tigeot drm_gtf2_k(struct edid *edid) 15805718399fSFrançois Tigeot { 15815718399fSFrançois Tigeot u8 *r = NULL; 15825718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15835718399fSFrançois Tigeot return r ? r[16] : 0; 15845718399fSFrançois Tigeot } 15855718399fSFrançois Tigeot 15865718399fSFrançois Tigeot static int 15875718399fSFrançois Tigeot drm_gtf2_2j(struct edid *edid) 15885718399fSFrançois Tigeot { 15895718399fSFrançois Tigeot u8 *r = NULL; 15905718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15915718399fSFrançois Tigeot return r ? r[17] : 0; 15925718399fSFrançois Tigeot } 15935718399fSFrançois Tigeot 15945718399fSFrançois Tigeot /** 15955718399fSFrançois Tigeot * standard_timing_level - get std. timing level(CVT/GTF/DMT) 15965718399fSFrançois Tigeot * @edid: EDID block to scan 15975718399fSFrançois Tigeot */ 15985718399fSFrançois Tigeot static int standard_timing_level(struct edid *edid) 15995718399fSFrançois Tigeot { 16005718399fSFrançois Tigeot if (edid->revision >= 2) { 16015718399fSFrançois Tigeot if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) 16025718399fSFrançois Tigeot return LEVEL_CVT; 16035718399fSFrançois Tigeot if (drm_gtf2_hbreak(edid)) 16045718399fSFrançois Tigeot return LEVEL_GTF2; 16055718399fSFrançois Tigeot return LEVEL_GTF; 16065718399fSFrançois Tigeot } 16075718399fSFrançois Tigeot return LEVEL_DMT; 16085718399fSFrançois Tigeot } 16095718399fSFrançois Tigeot 16105718399fSFrançois Tigeot /* 16115718399fSFrançois Tigeot * 0 is reserved. The spec says 0x01 fill for unused timings. Some old 16125718399fSFrançois Tigeot * monitors fill with ascii space (0x20) instead. 16135718399fSFrançois Tigeot */ 16145718399fSFrançois Tigeot static int 16155718399fSFrançois Tigeot bad_std_timing(u8 a, u8 b) 16165718399fSFrançois Tigeot { 16175718399fSFrançois Tigeot return (a == 0x00 && b == 0x00) || 16185718399fSFrançois Tigeot (a == 0x01 && b == 0x01) || 16195718399fSFrançois Tigeot (a == 0x20 && b == 0x20); 16205718399fSFrançois Tigeot } 16215718399fSFrançois Tigeot 16225718399fSFrançois Tigeot /** 16235718399fSFrançois Tigeot * drm_mode_std - convert standard mode info (width, height, refresh) into mode 1624ba55f2f5SFrançois Tigeot * @connector: connector of for the EDID block 1625ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 16265718399fSFrançois Tigeot * @t: standard timing params 16275718399fSFrançois Tigeot * 16285718399fSFrançois Tigeot * Take the standard timing params (in this case width, aspect, and refresh) 16295718399fSFrançois Tigeot * and convert them into a real mode using CVT/GTF/DMT. 16305718399fSFrançois Tigeot */ 16315718399fSFrançois Tigeot static struct drm_display_mode * 16325718399fSFrançois Tigeot drm_mode_std(struct drm_connector *connector, struct edid *edid, 1633ba55f2f5SFrançois Tigeot struct std_timing *t) 16345718399fSFrançois Tigeot { 16355718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 16365718399fSFrançois Tigeot struct drm_display_mode *m, *mode = NULL; 16375718399fSFrançois Tigeot int hsize, vsize; 16385718399fSFrançois Tigeot int vrefresh_rate; 16395718399fSFrançois Tigeot unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) 16405718399fSFrançois Tigeot >> EDID_TIMING_ASPECT_SHIFT; 16415718399fSFrançois Tigeot unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) 16425718399fSFrançois Tigeot >> EDID_TIMING_VFREQ_SHIFT; 16435718399fSFrançois Tigeot int timing_level = standard_timing_level(edid); 16445718399fSFrançois Tigeot 16455718399fSFrançois Tigeot if (bad_std_timing(t->hsize, t->vfreq_aspect)) 16465718399fSFrançois Tigeot return NULL; 16475718399fSFrançois Tigeot 16485718399fSFrançois Tigeot /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ 16495718399fSFrançois Tigeot hsize = t->hsize * 8 + 248; 16505718399fSFrançois Tigeot /* vrefresh_rate = vfreq + 60 */ 16515718399fSFrançois Tigeot vrefresh_rate = vfreq + 60; 16525718399fSFrançois Tigeot /* the vdisplay is calculated based on the aspect ratio */ 16535718399fSFrançois Tigeot if (aspect_ratio == 0) { 1654ba55f2f5SFrançois Tigeot if (edid->revision < 3) 16555718399fSFrançois Tigeot vsize = hsize; 16565718399fSFrançois Tigeot else 16575718399fSFrançois Tigeot vsize = (hsize * 10) / 16; 16585718399fSFrançois Tigeot } else if (aspect_ratio == 1) 16595718399fSFrançois Tigeot vsize = (hsize * 3) / 4; 16605718399fSFrançois Tigeot else if (aspect_ratio == 2) 16615718399fSFrançois Tigeot vsize = (hsize * 4) / 5; 16625718399fSFrançois Tigeot else 16635718399fSFrançois Tigeot vsize = (hsize * 9) / 16; 16645718399fSFrançois Tigeot 16655718399fSFrançois Tigeot /* HDTV hack, part 1 */ 16665718399fSFrançois Tigeot if (vrefresh_rate == 60 && 16675718399fSFrançois Tigeot ((hsize == 1360 && vsize == 765) || 16685718399fSFrançois Tigeot (hsize == 1368 && vsize == 769))) { 16695718399fSFrançois Tigeot hsize = 1366; 16705718399fSFrançois Tigeot vsize = 768; 16715718399fSFrançois Tigeot } 16725718399fSFrançois Tigeot 16735718399fSFrançois Tigeot /* 16745718399fSFrançois Tigeot * If this connector already has a mode for this size and refresh 16755718399fSFrançois Tigeot * rate (because it came from detailed or CVT info), use that 16765718399fSFrançois Tigeot * instead. This way we don't have to guess at interlace or 16775718399fSFrançois Tigeot * reduced blanking. 16785718399fSFrançois Tigeot */ 16795718399fSFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) 16805718399fSFrançois Tigeot if (m->hdisplay == hsize && m->vdisplay == vsize && 16815718399fSFrançois Tigeot drm_mode_vrefresh(m) == vrefresh_rate) 16825718399fSFrançois Tigeot return NULL; 16835718399fSFrançois Tigeot 16845718399fSFrançois Tigeot /* HDTV hack, part 2 */ 16855718399fSFrançois Tigeot if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) { 16865718399fSFrançois Tigeot mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0, 16875718399fSFrançois Tigeot false); 16885718399fSFrançois Tigeot mode->hdisplay = 1366; 16895718399fSFrançois Tigeot mode->hsync_start = mode->hsync_start - 1; 16905718399fSFrançois Tigeot mode->hsync_end = mode->hsync_end - 1; 16915718399fSFrançois Tigeot return mode; 16925718399fSFrançois Tigeot } 16935718399fSFrançois Tigeot 16945718399fSFrançois Tigeot /* check whether it can be found in default mode table */ 1695ce3d36d7SFrançois Tigeot if (drm_monitor_supports_rb(edid)) { 1696ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, 1697ce3d36d7SFrançois Tigeot true); 1698ce3d36d7SFrançois Tigeot if (mode) 1699ce3d36d7SFrançois Tigeot return mode; 1700ce3d36d7SFrançois Tigeot } 1701ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false); 17025718399fSFrançois Tigeot if (mode) 17035718399fSFrançois Tigeot return mode; 17045718399fSFrançois Tigeot 1705ce3d36d7SFrançois Tigeot /* okay, generate it */ 17065718399fSFrançois Tigeot switch (timing_level) { 17075718399fSFrançois Tigeot case LEVEL_DMT: 17085718399fSFrançois Tigeot break; 17095718399fSFrançois Tigeot case LEVEL_GTF: 17105718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 17115718399fSFrançois Tigeot break; 17125718399fSFrançois Tigeot case LEVEL_GTF2: 17135718399fSFrançois Tigeot /* 17145718399fSFrançois Tigeot * This is potentially wrong if there's ever a monitor with 17155718399fSFrançois Tigeot * more than one ranges section, each claiming a different 17165718399fSFrançois Tigeot * secondary GTF curve. Please don't do that. 17175718399fSFrançois Tigeot */ 17185718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 1719ce3d36d7SFrançois Tigeot if (!mode) 1720ce3d36d7SFrançois Tigeot return NULL; 17215718399fSFrançois Tigeot if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { 1722ce3d36d7SFrançois Tigeot drm_mode_destroy(dev, mode); 17235718399fSFrançois Tigeot mode = drm_gtf_mode_complex(dev, hsize, vsize, 17245718399fSFrançois Tigeot vrefresh_rate, 0, 0, 17255718399fSFrançois Tigeot drm_gtf2_m(edid), 17265718399fSFrançois Tigeot drm_gtf2_2c(edid), 17275718399fSFrançois Tigeot drm_gtf2_k(edid), 17285718399fSFrançois Tigeot drm_gtf2_2j(edid)); 17295718399fSFrançois Tigeot } 17305718399fSFrançois Tigeot break; 17315718399fSFrançois Tigeot case LEVEL_CVT: 17325718399fSFrançois Tigeot mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, 17335718399fSFrançois Tigeot false); 17345718399fSFrançois Tigeot break; 17355718399fSFrançois Tigeot } 17365718399fSFrançois Tigeot return mode; 17375718399fSFrançois Tigeot } 17385718399fSFrançois Tigeot 17395718399fSFrançois Tigeot /* 17405718399fSFrançois Tigeot * EDID is delightfully ambiguous about how interlaced modes are to be 17415718399fSFrançois Tigeot * encoded. Our internal representation is of frame height, but some 17425718399fSFrançois Tigeot * HDTV detailed timings are encoded as field height. 17435718399fSFrançois Tigeot * 17445718399fSFrançois Tigeot * The format list here is from CEA, in frame size. Technically we 17455718399fSFrançois Tigeot * should be checking refresh rate too. Whatever. 17465718399fSFrançois Tigeot */ 17475718399fSFrançois Tigeot static void 17485718399fSFrançois Tigeot drm_mode_do_interlace_quirk(struct drm_display_mode *mode, 17495718399fSFrançois Tigeot struct detailed_pixel_timing *pt) 17505718399fSFrançois Tigeot { 17515718399fSFrançois Tigeot int i; 17525718399fSFrançois Tigeot static const struct { 17535718399fSFrançois Tigeot int w, h; 17545718399fSFrançois Tigeot } cea_interlaced[] = { 17555718399fSFrançois Tigeot { 1920, 1080 }, 17565718399fSFrançois Tigeot { 720, 480 }, 17575718399fSFrançois Tigeot { 1440, 480 }, 17585718399fSFrançois Tigeot { 2880, 480 }, 17595718399fSFrançois Tigeot { 720, 576 }, 17605718399fSFrançois Tigeot { 1440, 576 }, 17615718399fSFrançois Tigeot { 2880, 576 }, 17625718399fSFrançois Tigeot }; 17635718399fSFrançois Tigeot 17645718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_INTERLACED)) 17655718399fSFrançois Tigeot return; 17665718399fSFrançois Tigeot 17676e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) { 17685718399fSFrançois Tigeot if ((mode->hdisplay == cea_interlaced[i].w) && 17695718399fSFrançois Tigeot (mode->vdisplay == cea_interlaced[i].h / 2)) { 17705718399fSFrançois Tigeot mode->vdisplay *= 2; 17715718399fSFrançois Tigeot mode->vsync_start *= 2; 17725718399fSFrançois Tigeot mode->vsync_end *= 2; 17735718399fSFrançois Tigeot mode->vtotal *= 2; 17745718399fSFrançois Tigeot mode->vtotal |= 1; 17755718399fSFrançois Tigeot } 17765718399fSFrançois Tigeot } 17775718399fSFrançois Tigeot 17785718399fSFrançois Tigeot mode->flags |= DRM_MODE_FLAG_INTERLACE; 17795718399fSFrançois Tigeot } 17805718399fSFrançois Tigeot 17815718399fSFrançois Tigeot /** 17825718399fSFrançois Tigeot * drm_mode_detailed - create a new mode from an EDID detailed timing section 17835718399fSFrançois Tigeot * @dev: DRM device (needed to create new mode) 17845718399fSFrançois Tigeot * @edid: EDID block 17855718399fSFrançois Tigeot * @timing: EDID detailed timing info 17865718399fSFrançois Tigeot * @quirks: quirks to apply 17875718399fSFrançois Tigeot * 17885718399fSFrançois Tigeot * An EDID detailed timing block contains enough info for us to create and 17895718399fSFrançois Tigeot * return a new struct drm_display_mode. 17905718399fSFrançois Tigeot */ 17915718399fSFrançois Tigeot static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, 17925718399fSFrançois Tigeot struct edid *edid, 17935718399fSFrançois Tigeot struct detailed_timing *timing, 17945718399fSFrançois Tigeot u32 quirks) 17955718399fSFrançois Tigeot { 17965718399fSFrançois Tigeot struct drm_display_mode *mode; 17975718399fSFrançois Tigeot struct detailed_pixel_timing *pt = &timing->data.pixel_data; 17985718399fSFrançois Tigeot unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; 17995718399fSFrançois Tigeot unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; 18005718399fSFrançois Tigeot unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; 18015718399fSFrançois Tigeot unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; 18025718399fSFrançois Tigeot unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; 18035718399fSFrançois Tigeot unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; 1804d82bf20eSFrançois Tigeot unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4; 18055718399fSFrançois Tigeot unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); 18065718399fSFrançois Tigeot 18075718399fSFrançois Tigeot /* ignore tiny modes */ 18085718399fSFrançois Tigeot if (hactive < 64 || vactive < 64) 18095718399fSFrançois Tigeot return NULL; 18105718399fSFrançois Tigeot 18115718399fSFrançois Tigeot if (pt->misc & DRM_EDID_PT_STEREO) { 18129edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("stereo mode not supported\n"); 18135718399fSFrançois Tigeot return NULL; 18145718399fSFrançois Tigeot } 18155718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { 18169edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("composite sync not supported\n"); 18175718399fSFrançois Tigeot } 18185718399fSFrançois Tigeot 18195718399fSFrançois Tigeot /* it is incorrect if hsync/vsync width is zero */ 18205718399fSFrançois Tigeot if (!hsync_pulse_width || !vsync_pulse_width) { 18215718399fSFrançois Tigeot DRM_DEBUG_KMS("Incorrect Detailed timing. " 18225718399fSFrançois Tigeot "Wrong Hsync/Vsync pulse width\n"); 18235718399fSFrançois Tigeot return NULL; 18245718399fSFrançois Tigeot } 18256e29dde8SFrançois Tigeot 18266e29dde8SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { 18276e29dde8SFrançois Tigeot mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); 18286e29dde8SFrançois Tigeot if (!mode) 18296e29dde8SFrançois Tigeot return NULL; 18306e29dde8SFrançois Tigeot 18316e29dde8SFrançois Tigeot goto set_size; 18326e29dde8SFrançois Tigeot } 18336e29dde8SFrançois Tigeot 18345718399fSFrançois Tigeot mode = drm_mode_create(dev); 18355718399fSFrançois Tigeot if (!mode) 18365718399fSFrançois Tigeot return NULL; 18375718399fSFrançois Tigeot 18385718399fSFrançois Tigeot if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) 18396e29dde8SFrançois Tigeot timing->pixel_clock = cpu_to_le16(1088); 18405718399fSFrançois Tigeot 18416e29dde8SFrançois Tigeot mode->clock = le16_to_cpu(timing->pixel_clock) * 10; 18425718399fSFrançois Tigeot 18435718399fSFrançois Tigeot mode->hdisplay = hactive; 18445718399fSFrançois Tigeot mode->hsync_start = mode->hdisplay + hsync_offset; 18455718399fSFrançois Tigeot mode->hsync_end = mode->hsync_start + hsync_pulse_width; 18465718399fSFrançois Tigeot mode->htotal = mode->hdisplay + hblank; 18475718399fSFrançois Tigeot 18485718399fSFrançois Tigeot mode->vdisplay = vactive; 18495718399fSFrançois Tigeot mode->vsync_start = mode->vdisplay + vsync_offset; 18505718399fSFrançois Tigeot mode->vsync_end = mode->vsync_start + vsync_pulse_width; 18515718399fSFrançois Tigeot mode->vtotal = mode->vdisplay + vblank; 18525718399fSFrançois Tigeot 18535718399fSFrançois Tigeot /* Some EDIDs have bogus h/vtotal values */ 18545718399fSFrançois Tigeot if (mode->hsync_end > mode->htotal) 18555718399fSFrançois Tigeot mode->htotal = mode->hsync_end + 1; 18565718399fSFrançois Tigeot if (mode->vsync_end > mode->vtotal) 18575718399fSFrançois Tigeot mode->vtotal = mode->vsync_end + 1; 18585718399fSFrançois Tigeot 18595718399fSFrançois Tigeot drm_mode_do_interlace_quirk(mode, pt); 18605718399fSFrançois Tigeot 18615718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { 18625718399fSFrançois Tigeot pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; 18635718399fSFrançois Tigeot } 18645718399fSFrançois Tigeot 18655718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? 18665718399fSFrançois Tigeot DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 18675718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? 18685718399fSFrançois Tigeot DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 18695718399fSFrançois Tigeot 18706e29dde8SFrançois Tigeot set_size: 18715718399fSFrançois Tigeot mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; 18725718399fSFrançois Tigeot mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; 18735718399fSFrançois Tigeot 18745718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_IN_CM) { 18755718399fSFrançois Tigeot mode->width_mm *= 10; 18765718399fSFrançois Tigeot mode->height_mm *= 10; 18775718399fSFrançois Tigeot } 18785718399fSFrançois Tigeot 18795718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { 18805718399fSFrançois Tigeot mode->width_mm = edid->width_cm * 10; 18815718399fSFrançois Tigeot mode->height_mm = edid->height_cm * 10; 18825718399fSFrançois Tigeot } 18835718399fSFrançois Tigeot 18846e29dde8SFrançois Tigeot mode->type = DRM_MODE_TYPE_DRIVER; 1885d82bf20eSFrançois Tigeot mode->vrefresh = drm_mode_vrefresh(mode); 18866e29dde8SFrançois Tigeot drm_mode_set_name(mode); 18876e29dde8SFrançois Tigeot 18885718399fSFrançois Tigeot return mode; 18895718399fSFrançois Tigeot } 18905718399fSFrançois Tigeot 18915718399fSFrançois Tigeot static bool 1892ce3d36d7SFrançois Tigeot mode_in_hsync_range(const struct drm_display_mode *mode, 18935718399fSFrançois Tigeot struct edid *edid, u8 *t) 18945718399fSFrançois Tigeot { 18955718399fSFrançois Tigeot int hsync, hmin, hmax; 18965718399fSFrançois Tigeot 18975718399fSFrançois Tigeot hmin = t[7]; 18985718399fSFrançois Tigeot if (edid->revision >= 4) 18995718399fSFrançois Tigeot hmin += ((t[4] & 0x04) ? 255 : 0); 19005718399fSFrançois Tigeot hmax = t[8]; 19015718399fSFrançois Tigeot if (edid->revision >= 4) 19025718399fSFrançois Tigeot hmax += ((t[4] & 0x08) ? 255 : 0); 19035718399fSFrançois Tigeot hsync = drm_mode_hsync(mode); 19045718399fSFrançois Tigeot 19055718399fSFrançois Tigeot return (hsync <= hmax && hsync >= hmin); 19065718399fSFrançois Tigeot } 19075718399fSFrançois Tigeot 19085718399fSFrançois Tigeot static bool 1909ce3d36d7SFrançois Tigeot mode_in_vsync_range(const struct drm_display_mode *mode, 19105718399fSFrançois Tigeot struct edid *edid, u8 *t) 19115718399fSFrançois Tigeot { 19125718399fSFrançois Tigeot int vsync, vmin, vmax; 19135718399fSFrançois Tigeot 19145718399fSFrançois Tigeot vmin = t[5]; 19155718399fSFrançois Tigeot if (edid->revision >= 4) 19165718399fSFrançois Tigeot vmin += ((t[4] & 0x01) ? 255 : 0); 19175718399fSFrançois Tigeot vmax = t[6]; 19185718399fSFrançois Tigeot if (edid->revision >= 4) 19195718399fSFrançois Tigeot vmax += ((t[4] & 0x02) ? 255 : 0); 19205718399fSFrançois Tigeot vsync = drm_mode_vrefresh(mode); 19215718399fSFrançois Tigeot 19225718399fSFrançois Tigeot return (vsync <= vmax && vsync >= vmin); 19235718399fSFrançois Tigeot } 19245718399fSFrançois Tigeot 19255718399fSFrançois Tigeot static u32 19265718399fSFrançois Tigeot range_pixel_clock(struct edid *edid, u8 *t) 19275718399fSFrançois Tigeot { 19285718399fSFrançois Tigeot /* unspecified */ 19295718399fSFrançois Tigeot if (t[9] == 0 || t[9] == 255) 19305718399fSFrançois Tigeot return 0; 19315718399fSFrançois Tigeot 19325718399fSFrançois Tigeot /* 1.4 with CVT support gives us real precision, yay */ 19335718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 19345718399fSFrançois Tigeot return (t[9] * 10000) - ((t[12] >> 2) * 250); 19355718399fSFrançois Tigeot 19365718399fSFrançois Tigeot /* 1.3 is pathetic, so fuzz up a bit */ 19375718399fSFrançois Tigeot return t[9] * 10000 + 5001; 19385718399fSFrançois Tigeot } 19395718399fSFrançois Tigeot 19405718399fSFrançois Tigeot static bool 1941ce3d36d7SFrançois Tigeot mode_in_range(const struct drm_display_mode *mode, struct edid *edid, 19425718399fSFrançois Tigeot struct detailed_timing *timing) 19435718399fSFrançois Tigeot { 19445718399fSFrançois Tigeot u32 max_clock; 19455718399fSFrançois Tigeot u8 *t = (u8 *)timing; 19465718399fSFrançois Tigeot 19475718399fSFrançois Tigeot if (!mode_in_hsync_range(mode, edid, t)) 19485718399fSFrançois Tigeot return false; 19495718399fSFrançois Tigeot 19505718399fSFrançois Tigeot if (!mode_in_vsync_range(mode, edid, t)) 19515718399fSFrançois Tigeot return false; 19525718399fSFrançois Tigeot 19535718399fSFrançois Tigeot if ((max_clock = range_pixel_clock(edid, t))) 19545718399fSFrançois Tigeot if (mode->clock > max_clock) 19555718399fSFrançois Tigeot return false; 19565718399fSFrançois Tigeot 19575718399fSFrançois Tigeot /* 1.4 max horizontal check */ 19585718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 19595718399fSFrançois Tigeot if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) 19605718399fSFrançois Tigeot return false; 19615718399fSFrançois Tigeot 19625718399fSFrançois Tigeot if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) 19635718399fSFrançois Tigeot return false; 19645718399fSFrançois Tigeot 19655718399fSFrançois Tigeot return true; 19665718399fSFrançois Tigeot } 19675718399fSFrançois Tigeot 19686e29dde8SFrançois Tigeot static bool valid_inferred_mode(const struct drm_connector *connector, 19696e29dde8SFrançois Tigeot const struct drm_display_mode *mode) 19706e29dde8SFrançois Tigeot { 19716e29dde8SFrançois Tigeot struct drm_display_mode *m; 19726e29dde8SFrançois Tigeot bool ok = false; 19736e29dde8SFrançois Tigeot 19746e29dde8SFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) { 19756e29dde8SFrançois Tigeot if (mode->hdisplay == m->hdisplay && 19766e29dde8SFrançois Tigeot mode->vdisplay == m->vdisplay && 19776e29dde8SFrançois Tigeot drm_mode_vrefresh(mode) == drm_mode_vrefresh(m)) 19786e29dde8SFrançois Tigeot return false; /* duplicated */ 19796e29dde8SFrançois Tigeot if (mode->hdisplay <= m->hdisplay && 19806e29dde8SFrançois Tigeot mode->vdisplay <= m->vdisplay) 19816e29dde8SFrançois Tigeot ok = true; 19826e29dde8SFrançois Tigeot } 19836e29dde8SFrançois Tigeot return ok; 19846e29dde8SFrançois Tigeot } 19856e29dde8SFrançois Tigeot 19865718399fSFrançois Tigeot static int 19876e29dde8SFrançois Tigeot drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, 19885718399fSFrançois Tigeot struct detailed_timing *timing) 19895718399fSFrançois Tigeot { 19905718399fSFrançois Tigeot int i, modes = 0; 19915718399fSFrançois Tigeot struct drm_display_mode *newmode; 19925718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 19935718399fSFrançois Tigeot 1994d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 19956e29dde8SFrançois Tigeot if (mode_in_range(drm_dmt_modes + i, edid, timing) && 19966e29dde8SFrançois Tigeot valid_inferred_mode(connector, drm_dmt_modes + i)) { 19975718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); 19985718399fSFrançois Tigeot if (newmode) { 19995718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 20005718399fSFrançois Tigeot modes++; 20015718399fSFrançois Tigeot } 20025718399fSFrançois Tigeot } 20035718399fSFrançois Tigeot } 20045718399fSFrançois Tigeot 20055718399fSFrançois Tigeot return modes; 20065718399fSFrançois Tigeot } 20075718399fSFrançois Tigeot 20086e29dde8SFrançois Tigeot /* fix up 1366x768 mode from 1368x768; 20096e29dde8SFrançois Tigeot * GFT/CVT can't express 1366 width which isn't dividable by 8 20106e29dde8SFrançois Tigeot */ 20116e29dde8SFrançois Tigeot static void fixup_mode_1366x768(struct drm_display_mode *mode) 20126e29dde8SFrançois Tigeot { 20136e29dde8SFrançois Tigeot if (mode->hdisplay == 1368 && mode->vdisplay == 768) { 20146e29dde8SFrançois Tigeot mode->hdisplay = 1366; 20156e29dde8SFrançois Tigeot mode->hsync_start--; 20166e29dde8SFrançois Tigeot mode->hsync_end--; 20176e29dde8SFrançois Tigeot drm_mode_set_name(mode); 20186e29dde8SFrançois Tigeot } 20196e29dde8SFrançois Tigeot } 20206e29dde8SFrançois Tigeot 20216e29dde8SFrançois Tigeot static int 20226e29dde8SFrançois Tigeot drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, 20236e29dde8SFrançois Tigeot struct detailed_timing *timing) 20246e29dde8SFrançois Tigeot { 20256e29dde8SFrançois Tigeot int i, modes = 0; 20266e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 20276e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 20286e29dde8SFrançois Tigeot 2029d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 20306e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 20316e29dde8SFrançois Tigeot newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0); 20326e29dde8SFrançois Tigeot if (!newmode) 20336e29dde8SFrançois Tigeot return modes; 20346e29dde8SFrançois Tigeot 20356e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 20366e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 20376e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 20386e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 20396e29dde8SFrançois Tigeot continue; 20406e29dde8SFrançois Tigeot } 20416e29dde8SFrançois Tigeot 20426e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 20436e29dde8SFrançois Tigeot modes++; 20446e29dde8SFrançois Tigeot } 20456e29dde8SFrançois Tigeot 20466e29dde8SFrançois Tigeot return modes; 20476e29dde8SFrançois Tigeot } 20486e29dde8SFrançois Tigeot 20496e29dde8SFrançois Tigeot static int 20506e29dde8SFrançois Tigeot drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, 20516e29dde8SFrançois Tigeot struct detailed_timing *timing) 20526e29dde8SFrançois Tigeot { 20536e29dde8SFrançois Tigeot int i, modes = 0; 20546e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 20556e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 20566e29dde8SFrançois Tigeot bool rb = drm_monitor_supports_rb(edid); 20576e29dde8SFrançois Tigeot 2058d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 20596e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 20606e29dde8SFrançois Tigeot newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0); 20616e29dde8SFrançois Tigeot if (!newmode) 20626e29dde8SFrançois Tigeot return modes; 20636e29dde8SFrançois Tigeot 20646e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 20656e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 20666e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 20676e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 20686e29dde8SFrançois Tigeot continue; 20696e29dde8SFrançois Tigeot } 20706e29dde8SFrançois Tigeot 20716e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 20726e29dde8SFrançois Tigeot modes++; 20736e29dde8SFrançois Tigeot } 20746e29dde8SFrançois Tigeot 20756e29dde8SFrançois Tigeot return modes; 20766e29dde8SFrançois Tigeot } 20776e29dde8SFrançois Tigeot 20785718399fSFrançois Tigeot static void 20795718399fSFrançois Tigeot do_inferred_modes(struct detailed_timing *timing, void *c) 20805718399fSFrançois Tigeot { 20815718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 20825718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 20836e29dde8SFrançois Tigeot struct detailed_data_monitor_range *range = &data->data.range; 20845718399fSFrançois Tigeot 20856e29dde8SFrançois Tigeot if (data->type != EDID_DETAIL_MONITOR_RANGE) 20866e29dde8SFrançois Tigeot return; 20876e29dde8SFrançois Tigeot 20886e29dde8SFrançois Tigeot closure->modes += drm_dmt_modes_for_range(closure->connector, 20896e29dde8SFrançois Tigeot closure->edid, 20906e29dde8SFrançois Tigeot timing); 20916e29dde8SFrançois Tigeot 20926e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 1)) 20936e29dde8SFrançois Tigeot return; /* GTF not defined yet */ 20946e29dde8SFrançois Tigeot 20956e29dde8SFrançois Tigeot switch (range->flags) { 20966e29dde8SFrançois Tigeot case 0x02: /* secondary gtf, XXX could do more */ 20976e29dde8SFrançois Tigeot case 0x00: /* default gtf */ 20985718399fSFrançois Tigeot closure->modes += drm_gtf_modes_for_range(closure->connector, 20995718399fSFrançois Tigeot closure->edid, 21005718399fSFrançois Tigeot timing); 21016e29dde8SFrançois Tigeot break; 21026e29dde8SFrançois Tigeot case 0x04: /* cvt, only in 1.4+ */ 21036e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 3)) 21046e29dde8SFrançois Tigeot break; 21056e29dde8SFrançois Tigeot 21066e29dde8SFrançois Tigeot closure->modes += drm_cvt_modes_for_range(closure->connector, 21076e29dde8SFrançois Tigeot closure->edid, 21086e29dde8SFrançois Tigeot timing); 21096e29dde8SFrançois Tigeot break; 21106e29dde8SFrançois Tigeot case 0x01: /* just the ranges, no formula */ 21116e29dde8SFrançois Tigeot default: 21126e29dde8SFrançois Tigeot break; 21136e29dde8SFrançois Tigeot } 21145718399fSFrançois Tigeot } 21155718399fSFrançois Tigeot 21165718399fSFrançois Tigeot static int 21175718399fSFrançois Tigeot add_inferred_modes(struct drm_connector *connector, struct edid *edid) 21185718399fSFrançois Tigeot { 21195718399fSFrançois Tigeot struct detailed_mode_closure closure = { 21205718399fSFrançois Tigeot connector, edid, 0, 0, 0 21215718399fSFrançois Tigeot }; 21225718399fSFrançois Tigeot 21235718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 21245718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, 21255718399fSFrançois Tigeot &closure); 21265718399fSFrançois Tigeot 21275718399fSFrançois Tigeot return closure.modes; 21285718399fSFrançois Tigeot } 21295718399fSFrançois Tigeot 21305718399fSFrançois Tigeot static int 21315718399fSFrançois Tigeot drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) 21325718399fSFrançois Tigeot { 21335718399fSFrançois Tigeot int i, j, m, modes = 0; 21345718399fSFrançois Tigeot struct drm_display_mode *mode; 21355718399fSFrançois Tigeot u8 *est = ((u8 *)timing) + 5; 21365718399fSFrançois Tigeot 21375718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 21389edbd4a0SFrançois Tigeot for (j = 7; j >= 0; j--) { 21395718399fSFrançois Tigeot m = (i * 8) + (7 - j); 2140ce3d36d7SFrançois Tigeot if (m >= ARRAY_SIZE(est3_modes)) 21415718399fSFrançois Tigeot break; 21425718399fSFrançois Tigeot if (est[i] & (1 << j)) { 21435718399fSFrançois Tigeot mode = drm_mode_find_dmt(connector->dev, 21445718399fSFrançois Tigeot est3_modes[m].w, 21455718399fSFrançois Tigeot est3_modes[m].h, 2146ce3d36d7SFrançois Tigeot est3_modes[m].r, 2147ce3d36d7SFrançois Tigeot est3_modes[m].rb); 21485718399fSFrançois Tigeot if (mode) { 21495718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 21505718399fSFrançois Tigeot modes++; 21515718399fSFrançois Tigeot } 21525718399fSFrançois Tigeot } 21535718399fSFrançois Tigeot } 21545718399fSFrançois Tigeot } 21555718399fSFrançois Tigeot 21565718399fSFrançois Tigeot return modes; 21575718399fSFrançois Tigeot } 21585718399fSFrançois Tigeot 21595718399fSFrançois Tigeot static void 21605718399fSFrançois Tigeot do_established_modes(struct detailed_timing *timing, void *c) 21615718399fSFrançois Tigeot { 21625718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 21635718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 21645718399fSFrançois Tigeot 21655718399fSFrançois Tigeot if (data->type == EDID_DETAIL_EST_TIMINGS) 21665718399fSFrançois Tigeot closure->modes += drm_est3_modes(closure->connector, timing); 21675718399fSFrançois Tigeot } 21685718399fSFrançois Tigeot 21695718399fSFrançois Tigeot /** 21705718399fSFrançois Tigeot * add_established_modes - get est. modes from EDID and add them 2171ba55f2f5SFrançois Tigeot * @connector: connector to add mode(s) to 21725718399fSFrançois Tigeot * @edid: EDID block to scan 21735718399fSFrançois Tigeot * 21745718399fSFrançois Tigeot * Each EDID block contains a bitmap of the supported "established modes" list 21755718399fSFrançois Tigeot * (defined above). Tease them out and add them to the global modes list. 21765718399fSFrançois Tigeot */ 21775718399fSFrançois Tigeot static int 21785718399fSFrançois Tigeot add_established_modes(struct drm_connector *connector, struct edid *edid) 21795718399fSFrançois Tigeot { 21805718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 21815718399fSFrançois Tigeot unsigned long est_bits = edid->established_timings.t1 | 21825718399fSFrançois Tigeot (edid->established_timings.t2 << 8) | 21835718399fSFrançois Tigeot ((edid->established_timings.mfg_rsvd & 0x80) << 9); 21845718399fSFrançois Tigeot int i, modes = 0; 21855718399fSFrançois Tigeot struct detailed_mode_closure closure = { 21865718399fSFrançois Tigeot connector, edid, 0, 0, 0 21875718399fSFrançois Tigeot }; 21885718399fSFrançois Tigeot 21895718399fSFrançois Tigeot for (i = 0; i <= EDID_EST_TIMINGS; i++) { 21905718399fSFrançois Tigeot if (est_bits & (1<<i)) { 21915718399fSFrançois Tigeot struct drm_display_mode *newmode; 21925718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); 21935718399fSFrançois Tigeot if (newmode) { 21945718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 21955718399fSFrançois Tigeot modes++; 21965718399fSFrançois Tigeot } 21975718399fSFrançois Tigeot } 21985718399fSFrançois Tigeot } 21995718399fSFrançois Tigeot 22005718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 22015718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, 22025718399fSFrançois Tigeot do_established_modes, &closure); 22035718399fSFrançois Tigeot 22045718399fSFrançois Tigeot return modes + closure.modes; 22055718399fSFrançois Tigeot } 22065718399fSFrançois Tigeot 22075718399fSFrançois Tigeot static void 22085718399fSFrançois Tigeot do_standard_modes(struct detailed_timing *timing, void *c) 22095718399fSFrançois Tigeot { 22105718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 22115718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 22125718399fSFrançois Tigeot struct drm_connector *connector = closure->connector; 22135718399fSFrançois Tigeot struct edid *edid = closure->edid; 22145718399fSFrançois Tigeot 22155718399fSFrançois Tigeot if (data->type == EDID_DETAIL_STD_MODES) { 22165718399fSFrançois Tigeot int i; 22175718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 22185718399fSFrançois Tigeot struct std_timing *std; 22195718399fSFrançois Tigeot struct drm_display_mode *newmode; 22205718399fSFrançois Tigeot 22215718399fSFrançois Tigeot std = &data->data.timings[i]; 2222ba55f2f5SFrançois Tigeot newmode = drm_mode_std(connector, edid, std); 22235718399fSFrançois Tigeot if (newmode) { 22245718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22255718399fSFrançois Tigeot closure->modes++; 22265718399fSFrançois Tigeot } 22275718399fSFrançois Tigeot } 22285718399fSFrançois Tigeot } 22295718399fSFrançois Tigeot } 22305718399fSFrançois Tigeot 22315718399fSFrançois Tigeot /** 22325718399fSFrançois Tigeot * add_standard_modes - get std. modes from EDID and add them 2233ba55f2f5SFrançois Tigeot * @connector: connector to add mode(s) to 22345718399fSFrançois Tigeot * @edid: EDID block to scan 22355718399fSFrançois Tigeot * 22365718399fSFrançois Tigeot * Standard modes can be calculated using the appropriate standard (DMT, 22375718399fSFrançois Tigeot * GTF or CVT. Grab them from @edid and add them to the list. 22385718399fSFrançois Tigeot */ 22395718399fSFrançois Tigeot static int 22405718399fSFrançois Tigeot add_standard_modes(struct drm_connector *connector, struct edid *edid) 22415718399fSFrançois Tigeot { 22425718399fSFrançois Tigeot int i, modes = 0; 22435718399fSFrançois Tigeot struct detailed_mode_closure closure = { 22445718399fSFrançois Tigeot connector, edid, 0, 0, 0 22455718399fSFrançois Tigeot }; 22465718399fSFrançois Tigeot 22475718399fSFrançois Tigeot for (i = 0; i < EDID_STD_TIMINGS; i++) { 22485718399fSFrançois Tigeot struct drm_display_mode *newmode; 22495718399fSFrançois Tigeot 22505718399fSFrançois Tigeot newmode = drm_mode_std(connector, edid, 2251ba55f2f5SFrançois Tigeot &edid->standard_timings[i]); 22525718399fSFrançois Tigeot if (newmode) { 22535718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22545718399fSFrançois Tigeot modes++; 22555718399fSFrançois Tigeot } 22565718399fSFrançois Tigeot } 22575718399fSFrançois Tigeot 22585718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 22595718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_standard_modes, 22605718399fSFrançois Tigeot &closure); 22615718399fSFrançois Tigeot 22625718399fSFrançois Tigeot /* XXX should also look for standard codes in VTB blocks */ 22635718399fSFrançois Tigeot 22645718399fSFrançois Tigeot return modes + closure.modes; 22655718399fSFrançois Tigeot } 22665718399fSFrançois Tigeot 22675718399fSFrançois Tigeot static int drm_cvt_modes(struct drm_connector *connector, 22685718399fSFrançois Tigeot struct detailed_timing *timing) 22695718399fSFrançois Tigeot { 22705718399fSFrançois Tigeot int i, j, modes = 0; 22715718399fSFrançois Tigeot struct drm_display_mode *newmode; 22725718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 22735718399fSFrançois Tigeot struct cvt_timing *cvt; 22745718399fSFrançois Tigeot const int rates[] = { 60, 85, 75, 60, 50 }; 22755718399fSFrançois Tigeot const u8 empty[3] = { 0, 0, 0 }; 22765718399fSFrançois Tigeot 22775718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 2278a3faafcbSSascha Wildner int width = 0, height; 22795718399fSFrançois Tigeot cvt = &(timing->data.other_data.data.cvt[i]); 22805718399fSFrançois Tigeot 22815718399fSFrançois Tigeot if (!memcmp(cvt->code, empty, 3)) 22825718399fSFrançois Tigeot continue; 22835718399fSFrançois Tigeot 22845718399fSFrançois Tigeot height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; 22855718399fSFrançois Tigeot switch (cvt->code[1] & 0x0c) { 22865718399fSFrançois Tigeot case 0x00: 22875718399fSFrançois Tigeot width = height * 4 / 3; 22885718399fSFrançois Tigeot break; 22895718399fSFrançois Tigeot case 0x04: 22905718399fSFrançois Tigeot width = height * 16 / 9; 22915718399fSFrançois Tigeot break; 22925718399fSFrançois Tigeot case 0x08: 22935718399fSFrançois Tigeot width = height * 16 / 10; 22945718399fSFrançois Tigeot break; 22955718399fSFrançois Tigeot case 0x0c: 22965718399fSFrançois Tigeot width = height * 15 / 9; 22975718399fSFrançois Tigeot break; 22985718399fSFrançois Tigeot } 22995718399fSFrançois Tigeot 23005718399fSFrançois Tigeot for (j = 1; j < 5; j++) { 23015718399fSFrançois Tigeot if (cvt->code[2] & (1 << j)) { 23025718399fSFrançois Tigeot newmode = drm_cvt_mode(dev, width, height, 23035718399fSFrançois Tigeot rates[j], j == 0, 23045718399fSFrançois Tigeot false, false); 23055718399fSFrançois Tigeot if (newmode) { 23065718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 23075718399fSFrançois Tigeot modes++; 23085718399fSFrançois Tigeot } 23095718399fSFrançois Tigeot } 23105718399fSFrançois Tigeot } 23115718399fSFrançois Tigeot } 23125718399fSFrançois Tigeot 23135718399fSFrançois Tigeot return modes; 23145718399fSFrançois Tigeot } 23155718399fSFrançois Tigeot 23165718399fSFrançois Tigeot static void 23175718399fSFrançois Tigeot do_cvt_mode(struct detailed_timing *timing, void *c) 23185718399fSFrançois Tigeot { 23195718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 23205718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 23215718399fSFrançois Tigeot 23225718399fSFrançois Tigeot if (data->type == EDID_DETAIL_CVT_3BYTE) 23235718399fSFrançois Tigeot closure->modes += drm_cvt_modes(closure->connector, timing); 23245718399fSFrançois Tigeot } 23255718399fSFrançois Tigeot 23265718399fSFrançois Tigeot static int 23275718399fSFrançois Tigeot add_cvt_modes(struct drm_connector *connector, struct edid *edid) 23285718399fSFrançois Tigeot { 23295718399fSFrançois Tigeot struct detailed_mode_closure closure = { 23305718399fSFrançois Tigeot connector, edid, 0, 0, 0 23315718399fSFrançois Tigeot }; 23325718399fSFrançois Tigeot 23335718399fSFrançois Tigeot if (version_greater(edid, 1, 2)) 23345718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); 23355718399fSFrançois Tigeot 23365718399fSFrançois Tigeot /* XXX should also look for CVT codes in VTB blocks */ 23375718399fSFrançois Tigeot 23385718399fSFrançois Tigeot return closure.modes; 23395718399fSFrançois Tigeot } 23405718399fSFrançois Tigeot 23415718399fSFrançois Tigeot static void 23425718399fSFrançois Tigeot do_detailed_mode(struct detailed_timing *timing, void *c) 23435718399fSFrançois Tigeot { 23445718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 23455718399fSFrançois Tigeot struct drm_display_mode *newmode; 23465718399fSFrançois Tigeot 23475718399fSFrançois Tigeot if (timing->pixel_clock) { 23485718399fSFrançois Tigeot newmode = drm_mode_detailed(closure->connector->dev, 23495718399fSFrançois Tigeot closure->edid, timing, 23505718399fSFrançois Tigeot closure->quirks); 23515718399fSFrançois Tigeot if (!newmode) 23525718399fSFrançois Tigeot return; 23535718399fSFrançois Tigeot 23545718399fSFrançois Tigeot if (closure->preferred) 23555718399fSFrançois Tigeot newmode->type |= DRM_MODE_TYPE_PREFERRED; 23565718399fSFrançois Tigeot 23575718399fSFrançois Tigeot drm_mode_probed_add(closure->connector, newmode); 23585718399fSFrançois Tigeot closure->modes++; 23595718399fSFrançois Tigeot closure->preferred = 0; 23605718399fSFrançois Tigeot } 23615718399fSFrançois Tigeot } 23625718399fSFrançois Tigeot 23635718399fSFrançois Tigeot /* 23645718399fSFrançois Tigeot * add_detailed_modes - Add modes from detailed timings 23655718399fSFrançois Tigeot * @connector: attached connector 23665718399fSFrançois Tigeot * @edid: EDID block to scan 23675718399fSFrançois Tigeot * @quirks: quirks to apply 23685718399fSFrançois Tigeot */ 23695718399fSFrançois Tigeot static int 23705718399fSFrançois Tigeot add_detailed_modes(struct drm_connector *connector, struct edid *edid, 23715718399fSFrançois Tigeot u32 quirks) 23725718399fSFrançois Tigeot { 23735718399fSFrançois Tigeot struct detailed_mode_closure closure = { 23745718399fSFrançois Tigeot connector, 23755718399fSFrançois Tigeot edid, 23765718399fSFrançois Tigeot 1, 23775718399fSFrançois Tigeot quirks, 23785718399fSFrançois Tigeot 0 23795718399fSFrançois Tigeot }; 23805718399fSFrançois Tigeot 23815718399fSFrançois Tigeot if (closure.preferred && !version_greater(edid, 1, 3)) 23825718399fSFrançois Tigeot closure.preferred = 23835718399fSFrançois Tigeot (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); 23845718399fSFrançois Tigeot 23855718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); 23865718399fSFrançois Tigeot 23875718399fSFrançois Tigeot return closure.modes; 23885718399fSFrançois Tigeot } 23895718399fSFrançois Tigeot 23905718399fSFrançois Tigeot #define AUDIO_BLOCK 0x01 23916e29dde8SFrançois Tigeot #define VIDEO_BLOCK 0x02 23925718399fSFrançois Tigeot #define VENDOR_BLOCK 0x03 23935718399fSFrançois Tigeot #define SPEAKER_BLOCK 0x04 2394a2fdbec6SFrançois Tigeot #define VIDEO_CAPABILITY_BLOCK 0x07 23955718399fSFrançois Tigeot #define EDID_BASIC_AUDIO (1 << 6) 23966e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB444 (1 << 5) 23976e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB422 (1 << 4) 2398a2fdbec6SFrançois Tigeot #define EDID_CEA_VCDB_QS (1 << 6) 23995718399fSFrançois Tigeot 24009edbd4a0SFrançois Tigeot /* 24015718399fSFrançois Tigeot * Search EDID for CEA extension block. 24025718399fSFrançois Tigeot */ 24039edbd4a0SFrançois Tigeot static u8 *drm_find_cea_extension(struct edid *edid) 24045718399fSFrançois Tigeot { 24055718399fSFrançois Tigeot u8 *edid_ext = NULL; 24065718399fSFrançois Tigeot int i; 24075718399fSFrançois Tigeot 24085718399fSFrançois Tigeot /* No EDID or EDID extensions */ 24095718399fSFrançois Tigeot if (edid == NULL || edid->extensions == 0) 24105718399fSFrançois Tigeot return NULL; 24115718399fSFrançois Tigeot 24125718399fSFrançois Tigeot /* Find CEA extension */ 24135718399fSFrançois Tigeot for (i = 0; i < edid->extensions; i++) { 24145718399fSFrançois Tigeot edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); 24155718399fSFrançois Tigeot if (edid_ext[0] == CEA_EXT) 24165718399fSFrançois Tigeot break; 24175718399fSFrançois Tigeot } 24185718399fSFrançois Tigeot 24195718399fSFrançois Tigeot if (i == edid->extensions) 24205718399fSFrançois Tigeot return NULL; 24215718399fSFrançois Tigeot 24225718399fSFrançois Tigeot return edid_ext; 24235718399fSFrançois Tigeot } 24249edbd4a0SFrançois Tigeot 24259edbd4a0SFrançois Tigeot /* 24269edbd4a0SFrançois Tigeot * Calculate the alternate clock for the CEA mode 24279edbd4a0SFrançois Tigeot * (60Hz vs. 59.94Hz etc.) 24289edbd4a0SFrançois Tigeot */ 24299edbd4a0SFrançois Tigeot static unsigned int 24309edbd4a0SFrançois Tigeot cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) 24319edbd4a0SFrançois Tigeot { 24329edbd4a0SFrançois Tigeot unsigned int clock = cea_mode->clock; 24339edbd4a0SFrançois Tigeot 24349edbd4a0SFrançois Tigeot if (cea_mode->vrefresh % 6 != 0) 24359edbd4a0SFrançois Tigeot return clock; 24369edbd4a0SFrançois Tigeot 24379edbd4a0SFrançois Tigeot /* 24389edbd4a0SFrançois Tigeot * edid_cea_modes contains the 59.94Hz 24399edbd4a0SFrançois Tigeot * variant for 240 and 480 line modes, 24409edbd4a0SFrançois Tigeot * and the 60Hz variant otherwise. 24419edbd4a0SFrançois Tigeot */ 24429edbd4a0SFrançois Tigeot if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480) 24439edbd4a0SFrançois Tigeot clock = clock * 1001 / 1000; 24449edbd4a0SFrançois Tigeot else 24459edbd4a0SFrançois Tigeot clock = DIV_ROUND_UP(clock * 1000, 1001); 24469edbd4a0SFrançois Tigeot 24479edbd4a0SFrançois Tigeot return clock; 24489edbd4a0SFrançois Tigeot } 24495718399fSFrançois Tigeot 2450d82bf20eSFrançois Tigeot /** 2451d82bf20eSFrançois Tigeot * drm_match_cea_mode - look for a CEA mode matching given mode 2452d82bf20eSFrançois Tigeot * @to_match: display mode 2453d82bf20eSFrançois Tigeot * 2454ba55f2f5SFrançois Tigeot * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 2455d82bf20eSFrançois Tigeot * mode. 24566e29dde8SFrançois Tigeot */ 2457a2fdbec6SFrançois Tigeot u8 drm_match_cea_mode(const struct drm_display_mode *to_match) 24585718399fSFrançois Tigeot { 24596e29dde8SFrançois Tigeot u8 mode; 24606e29dde8SFrançois Tigeot 24619edbd4a0SFrançois Tigeot if (!to_match->clock) 24629edbd4a0SFrançois Tigeot return 0; 24636e29dde8SFrançois Tigeot 24649edbd4a0SFrançois Tigeot for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) { 24659edbd4a0SFrançois Tigeot const struct drm_display_mode *cea_mode = &edid_cea_modes[mode]; 24669edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 24679edbd4a0SFrançois Tigeot 24689edbd4a0SFrançois Tigeot /* Check both 60Hz and 59.94Hz */ 24699edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 24709edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 24719edbd4a0SFrançois Tigeot 24729edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 24739edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 24749edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode)) 24756e29dde8SFrançois Tigeot return mode + 1; 24766e29dde8SFrançois Tigeot } 24776e29dde8SFrançois Tigeot return 0; 24786e29dde8SFrançois Tigeot } 24796e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_match_cea_mode); 24806e29dde8SFrançois Tigeot 2481ba55f2f5SFrançois Tigeot /** 2482ba55f2f5SFrançois Tigeot * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to 2483ba55f2f5SFrançois Tigeot * the input VIC from the CEA mode list 2484ba55f2f5SFrançois Tigeot * @video_code: ID given to each of the CEA modes 2485ba55f2f5SFrançois Tigeot * 2486ba55f2f5SFrançois Tigeot * Returns picture aspect ratio 2487ba55f2f5SFrançois Tigeot */ 2488ba55f2f5SFrançois Tigeot enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) 2489ba55f2f5SFrançois Tigeot { 2490ba55f2f5SFrançois Tigeot /* return picture aspect ratio for video_code - 1 to access the 2491ba55f2f5SFrançois Tigeot * right array element 2492ba55f2f5SFrançois Tigeot */ 2493ba55f2f5SFrançois Tigeot return edid_cea_modes[video_code-1].picture_aspect_ratio; 2494ba55f2f5SFrançois Tigeot } 2495ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_cea_aspect_ratio); 2496ba55f2f5SFrançois Tigeot 24979edbd4a0SFrançois Tigeot /* 24989edbd4a0SFrançois Tigeot * Calculate the alternate clock for HDMI modes (those from the HDMI vendor 24999edbd4a0SFrançois Tigeot * specific block). 25009edbd4a0SFrançois Tigeot * 25019edbd4a0SFrançois Tigeot * It's almost like cea_mode_alternate_clock(), we just need to add an 25029edbd4a0SFrançois Tigeot * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this 25039edbd4a0SFrançois Tigeot * one. 25049edbd4a0SFrançois Tigeot */ 25059edbd4a0SFrançois Tigeot static unsigned int 25069edbd4a0SFrançois Tigeot hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode) 25079edbd4a0SFrançois Tigeot { 25089edbd4a0SFrançois Tigeot if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160) 25099edbd4a0SFrançois Tigeot return hdmi_mode->clock; 25109edbd4a0SFrançois Tigeot 25119edbd4a0SFrançois Tigeot return cea_mode_alternate_clock(hdmi_mode); 25129edbd4a0SFrançois Tigeot } 25139edbd4a0SFrançois Tigeot 25149edbd4a0SFrançois Tigeot /* 25159edbd4a0SFrançois Tigeot * drm_match_hdmi_mode - look for a HDMI mode matching given mode 25169edbd4a0SFrançois Tigeot * @to_match: display mode 25179edbd4a0SFrançois Tigeot * 25189edbd4a0SFrançois Tigeot * An HDMI mode is one defined in the HDMI vendor specific block. 25199edbd4a0SFrançois Tigeot * 25209edbd4a0SFrançois Tigeot * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one. 25219edbd4a0SFrançois Tigeot */ 25229edbd4a0SFrançois Tigeot static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match) 25239edbd4a0SFrançois Tigeot { 25249edbd4a0SFrançois Tigeot u8 mode; 25259edbd4a0SFrançois Tigeot 25269edbd4a0SFrançois Tigeot if (!to_match->clock) 25279edbd4a0SFrançois Tigeot return 0; 25289edbd4a0SFrançois Tigeot 25299edbd4a0SFrançois Tigeot for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) { 25309edbd4a0SFrançois Tigeot const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode]; 25319edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 25329edbd4a0SFrançois Tigeot 25339edbd4a0SFrançois Tigeot /* Make sure to also match alternate clocks */ 25349edbd4a0SFrançois Tigeot clock1 = hdmi_mode->clock; 25359edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(hdmi_mode); 25369edbd4a0SFrançois Tigeot 25379edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 25389edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 25399edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode)) 25409edbd4a0SFrançois Tigeot return mode + 1; 25419edbd4a0SFrançois Tigeot } 25429edbd4a0SFrançois Tigeot return 0; 25439edbd4a0SFrançois Tigeot } 25446e29dde8SFrançois Tigeot 25456e29dde8SFrançois Tigeot static int 25469edbd4a0SFrançois Tigeot add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) 25476e29dde8SFrançois Tigeot { 25486e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 25499edbd4a0SFrançois Tigeot struct drm_display_mode *mode, *tmp; 25509edbd4a0SFrançois Tigeot LINUX_LIST_HEAD(list); 25516e29dde8SFrançois Tigeot int modes = 0; 25526e29dde8SFrançois Tigeot 25539edbd4a0SFrançois Tigeot /* Don't add CEA modes if the CEA extension block is missing */ 25549edbd4a0SFrançois Tigeot if (!drm_find_cea_extension(edid)) 25559edbd4a0SFrançois Tigeot return 0; 25569edbd4a0SFrançois Tigeot 25579edbd4a0SFrançois Tigeot /* 25589edbd4a0SFrançois Tigeot * Go through all probed modes and create a new mode 25599edbd4a0SFrançois Tigeot * with the alternate clock for certain CEA modes. 25609edbd4a0SFrançois Tigeot */ 25619edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 25629edbd4a0SFrançois Tigeot const struct drm_display_mode *cea_mode = NULL; 25636e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 25649edbd4a0SFrançois Tigeot u8 mode_idx = drm_match_cea_mode(mode) - 1; 25659edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 25669edbd4a0SFrançois Tigeot 25679edbd4a0SFrançois Tigeot if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { 25689edbd4a0SFrançois Tigeot cea_mode = &edid_cea_modes[mode_idx]; 25699edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 25709edbd4a0SFrançois Tigeot } else { 25719edbd4a0SFrançois Tigeot mode_idx = drm_match_hdmi_mode(mode) - 1; 25729edbd4a0SFrançois Tigeot if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { 25739edbd4a0SFrançois Tigeot cea_mode = &edid_4k_modes[mode_idx]; 25749edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(cea_mode); 25759edbd4a0SFrançois Tigeot } 25769edbd4a0SFrançois Tigeot } 25779edbd4a0SFrançois Tigeot 25789edbd4a0SFrançois Tigeot if (!cea_mode) 25799edbd4a0SFrançois Tigeot continue; 25809edbd4a0SFrançois Tigeot 25819edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 25829edbd4a0SFrançois Tigeot 25839edbd4a0SFrançois Tigeot if (clock1 == clock2) 25849edbd4a0SFrançois Tigeot continue; 25859edbd4a0SFrançois Tigeot 25869edbd4a0SFrançois Tigeot if (mode->clock != clock1 && mode->clock != clock2) 25879edbd4a0SFrançois Tigeot continue; 25889edbd4a0SFrançois Tigeot 25899edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, cea_mode); 25909edbd4a0SFrançois Tigeot if (!newmode) 25919edbd4a0SFrançois Tigeot continue; 25929edbd4a0SFrançois Tigeot 25939edbd4a0SFrançois Tigeot /* Carry over the stereo flags */ 25949edbd4a0SFrançois Tigeot newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK; 25959edbd4a0SFrançois Tigeot 25969edbd4a0SFrançois Tigeot /* 25979edbd4a0SFrançois Tigeot * The current mode could be either variant. Make 25989edbd4a0SFrançois Tigeot * sure to pick the "other" clock for the new mode. 25999edbd4a0SFrançois Tigeot */ 26009edbd4a0SFrançois Tigeot if (mode->clock != clock1) 26019edbd4a0SFrançois Tigeot newmode->clock = clock1; 26029edbd4a0SFrançois Tigeot else 26039edbd4a0SFrançois Tigeot newmode->clock = clock2; 26049edbd4a0SFrançois Tigeot 26059edbd4a0SFrançois Tigeot list_add_tail(&newmode->head, &list); 26069edbd4a0SFrançois Tigeot } 26079edbd4a0SFrançois Tigeot 26089edbd4a0SFrançois Tigeot list_for_each_entry_safe(mode, tmp, &list, head) { 26099edbd4a0SFrançois Tigeot list_del(&mode->head); 26109edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 26119edbd4a0SFrançois Tigeot modes++; 26129edbd4a0SFrançois Tigeot } 26139edbd4a0SFrançois Tigeot 26149edbd4a0SFrançois Tigeot return modes; 26159edbd4a0SFrançois Tigeot } 26169edbd4a0SFrançois Tigeot 26179edbd4a0SFrançois Tigeot static struct drm_display_mode * 26189edbd4a0SFrançois Tigeot drm_display_mode_from_vic_index(struct drm_connector *connector, 26199edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, 26209edbd4a0SFrançois Tigeot u8 video_index) 26219edbd4a0SFrançois Tigeot { 26229edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 26239edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 26249edbd4a0SFrançois Tigeot u8 cea_mode; 26259edbd4a0SFrançois Tigeot 26269edbd4a0SFrançois Tigeot if (video_db == NULL || video_index >= video_len) 26279edbd4a0SFrançois Tigeot return NULL; 26289edbd4a0SFrançois Tigeot 26299edbd4a0SFrançois Tigeot /* CEA modes are numbered 1..127 */ 26309edbd4a0SFrançois Tigeot cea_mode = (video_db[video_index] & 127) - 1; 26319edbd4a0SFrançois Tigeot if (cea_mode >= ARRAY_SIZE(edid_cea_modes)) 26329edbd4a0SFrançois Tigeot return NULL; 26339edbd4a0SFrançois Tigeot 26349edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); 2635ba55f2f5SFrançois Tigeot if (!newmode) 2636ba55f2f5SFrançois Tigeot return NULL; 2637ba55f2f5SFrançois Tigeot 26389edbd4a0SFrançois Tigeot newmode->vrefresh = 0; 26399edbd4a0SFrançois Tigeot 26409edbd4a0SFrançois Tigeot return newmode; 26419edbd4a0SFrançois Tigeot } 26429edbd4a0SFrançois Tigeot 26439edbd4a0SFrançois Tigeot static int 26449edbd4a0SFrançois Tigeot do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) 26459edbd4a0SFrançois Tigeot { 26469edbd4a0SFrançois Tigeot int i, modes = 0; 26479edbd4a0SFrançois Tigeot 26489edbd4a0SFrançois Tigeot for (i = 0; i < len; i++) { 26499edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 26509edbd4a0SFrançois Tigeot mode = drm_display_mode_from_vic_index(connector, db, len, i); 26519edbd4a0SFrançois Tigeot if (mode) { 26529edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 26539edbd4a0SFrançois Tigeot modes++; 26549edbd4a0SFrançois Tigeot } 26559edbd4a0SFrançois Tigeot } 26569edbd4a0SFrançois Tigeot 26579edbd4a0SFrançois Tigeot return modes; 26589edbd4a0SFrançois Tigeot } 26599edbd4a0SFrançois Tigeot 26609edbd4a0SFrançois Tigeot struct stereo_mandatory_mode { 26619edbd4a0SFrançois Tigeot int width, height, vrefresh; 26629edbd4a0SFrançois Tigeot unsigned int flags; 26639edbd4a0SFrançois Tigeot }; 26649edbd4a0SFrançois Tigeot 26659edbd4a0SFrançois Tigeot static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { 26669edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 26679edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, 26689edbd4a0SFrançois Tigeot { 1920, 1080, 50, 26699edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 26709edbd4a0SFrançois Tigeot { 1920, 1080, 60, 26719edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 26729edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 26739edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, 26749edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 26759edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } 26769edbd4a0SFrançois Tigeot }; 26779edbd4a0SFrançois Tigeot 26789edbd4a0SFrançois Tigeot static bool 26799edbd4a0SFrançois Tigeot stereo_match_mandatory(const struct drm_display_mode *mode, 26809edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *stereo_mode) 26819edbd4a0SFrançois Tigeot { 26829edbd4a0SFrançois Tigeot unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; 26839edbd4a0SFrançois Tigeot 26849edbd4a0SFrançois Tigeot return mode->hdisplay == stereo_mode->width && 26859edbd4a0SFrançois Tigeot mode->vdisplay == stereo_mode->height && 26869edbd4a0SFrançois Tigeot interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && 26879edbd4a0SFrançois Tigeot drm_mode_vrefresh(mode) == stereo_mode->vrefresh; 26889edbd4a0SFrançois Tigeot } 26899edbd4a0SFrançois Tigeot 26909edbd4a0SFrançois Tigeot static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) 26919edbd4a0SFrançois Tigeot { 26929edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 26939edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 26949edbd4a0SFrançois Tigeot struct list_head stereo_modes; 26959edbd4a0SFrançois Tigeot int modes = 0, i; 26969edbd4a0SFrançois Tigeot 26979edbd4a0SFrançois Tigeot INIT_LIST_HEAD(&stereo_modes); 26989edbd4a0SFrançois Tigeot 26999edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 27009edbd4a0SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { 27019edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *mandatory; 27029edbd4a0SFrançois Tigeot struct drm_display_mode *new_mode; 27039edbd4a0SFrançois Tigeot 27049edbd4a0SFrançois Tigeot if (!stereo_match_mandatory(mode, 27059edbd4a0SFrançois Tigeot &stereo_mandatory_modes[i])) 27069edbd4a0SFrançois Tigeot continue; 27079edbd4a0SFrançois Tigeot 27089edbd4a0SFrançois Tigeot mandatory = &stereo_mandatory_modes[i]; 27099edbd4a0SFrançois Tigeot new_mode = drm_mode_duplicate(dev, mode); 27109edbd4a0SFrançois Tigeot if (!new_mode) 27119edbd4a0SFrançois Tigeot continue; 27129edbd4a0SFrançois Tigeot 27139edbd4a0SFrançois Tigeot new_mode->flags |= mandatory->flags; 27149edbd4a0SFrançois Tigeot list_add_tail(&new_mode->head, &stereo_modes); 27159edbd4a0SFrançois Tigeot modes++; 27169edbd4a0SFrançois Tigeot } 27179edbd4a0SFrançois Tigeot } 27189edbd4a0SFrançois Tigeot 27199edbd4a0SFrançois Tigeot list_splice_tail(&stereo_modes, &connector->probed_modes); 27209edbd4a0SFrançois Tigeot 27219edbd4a0SFrançois Tigeot return modes; 27229edbd4a0SFrançois Tigeot } 27239edbd4a0SFrançois Tigeot 27249edbd4a0SFrançois Tigeot static int add_hdmi_mode(struct drm_connector *connector, u8 vic) 27259edbd4a0SFrançois Tigeot { 27269edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 27279edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 27289edbd4a0SFrançois Tigeot 27299edbd4a0SFrançois Tigeot vic--; /* VICs start at 1 */ 27309edbd4a0SFrançois Tigeot if (vic >= ARRAY_SIZE(edid_4k_modes)) { 27319edbd4a0SFrançois Tigeot DRM_ERROR("Unknown HDMI VIC: %d\n", vic); 27329edbd4a0SFrançois Tigeot return 0; 27339edbd4a0SFrançois Tigeot } 27349edbd4a0SFrançois Tigeot 27359edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); 27369edbd4a0SFrançois Tigeot if (!newmode) 27379edbd4a0SFrançois Tigeot return 0; 27389edbd4a0SFrançois Tigeot 27399edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 27409edbd4a0SFrançois Tigeot 27419edbd4a0SFrançois Tigeot return 1; 27429edbd4a0SFrançois Tigeot } 27439edbd4a0SFrançois Tigeot 27449edbd4a0SFrançois Tigeot static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, 27459edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, u8 video_index) 27469edbd4a0SFrançois Tigeot { 27479edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 27489edbd4a0SFrançois Tigeot int modes = 0; 27499edbd4a0SFrançois Tigeot 27509edbd4a0SFrançois Tigeot if (structure & (1 << 0)) { 27519edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 27529edbd4a0SFrançois Tigeot video_len, 27539edbd4a0SFrançois Tigeot video_index); 27546e29dde8SFrançois Tigeot if (newmode) { 27559edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; 27566e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 27576e29dde8SFrançois Tigeot modes++; 27586e29dde8SFrançois Tigeot } 27596e29dde8SFrançois Tigeot } 27609edbd4a0SFrançois Tigeot if (structure & (1 << 6)) { 27619edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 27629edbd4a0SFrançois Tigeot video_len, 27639edbd4a0SFrançois Tigeot video_index); 27649edbd4a0SFrançois Tigeot if (newmode) { 27659edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 27669edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 27679edbd4a0SFrançois Tigeot modes++; 27689edbd4a0SFrançois Tigeot } 27699edbd4a0SFrançois Tigeot } 27709edbd4a0SFrançois Tigeot if (structure & (1 << 8)) { 27719edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 27729edbd4a0SFrançois Tigeot video_len, 27739edbd4a0SFrançois Tigeot video_index); 27749edbd4a0SFrançois Tigeot if (newmode) { 27759edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 27769edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 27779edbd4a0SFrançois Tigeot modes++; 27789edbd4a0SFrançois Tigeot } 27796e29dde8SFrançois Tigeot } 27806e29dde8SFrançois Tigeot 27816e29dde8SFrançois Tigeot return modes; 27826e29dde8SFrançois Tigeot } 27836e29dde8SFrançois Tigeot 27849edbd4a0SFrançois Tigeot /* 27859edbd4a0SFrançois Tigeot * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block 27869edbd4a0SFrançois Tigeot * @connector: connector corresponding to the HDMI sink 27879edbd4a0SFrançois Tigeot * @db: start of the CEA vendor specific block 27889edbd4a0SFrançois Tigeot * @len: length of the CEA block payload, ie. one can access up to db[len] 27899edbd4a0SFrançois Tigeot * 27909edbd4a0SFrançois Tigeot * Parses the HDMI VSDB looking for modes to add to @connector. This function 27919edbd4a0SFrançois Tigeot * also adds the stereo 3d modes when applicable. 27929edbd4a0SFrançois Tigeot */ 27939edbd4a0SFrançois Tigeot static int 27949edbd4a0SFrançois Tigeot do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, 27959edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len) 27969edbd4a0SFrançois Tigeot { 27979edbd4a0SFrançois Tigeot int modes = 0, offset = 0, i, multi_present = 0, multi_len; 27989edbd4a0SFrançois Tigeot u8 vic_len, hdmi_3d_len = 0; 27999edbd4a0SFrançois Tigeot u16 mask; 28009edbd4a0SFrançois Tigeot u16 structure_all; 28019edbd4a0SFrançois Tigeot 28029edbd4a0SFrançois Tigeot if (len < 8) 28039edbd4a0SFrançois Tigeot goto out; 28049edbd4a0SFrançois Tigeot 28059edbd4a0SFrançois Tigeot /* no HDMI_Video_Present */ 28069edbd4a0SFrançois Tigeot if (!(db[8] & (1 << 5))) 28079edbd4a0SFrançois Tigeot goto out; 28089edbd4a0SFrançois Tigeot 28099edbd4a0SFrançois Tigeot /* Latency_Fields_Present */ 28109edbd4a0SFrançois Tigeot if (db[8] & (1 << 7)) 28119edbd4a0SFrançois Tigeot offset += 2; 28129edbd4a0SFrançois Tigeot 28139edbd4a0SFrançois Tigeot /* I_Latency_Fields_Present */ 28149edbd4a0SFrançois Tigeot if (db[8] & (1 << 6)) 28159edbd4a0SFrançois Tigeot offset += 2; 28169edbd4a0SFrançois Tigeot 28179edbd4a0SFrançois Tigeot /* the declared length is not long enough for the 2 first bytes 28189edbd4a0SFrançois Tigeot * of additional video format capabilities */ 28199edbd4a0SFrançois Tigeot if (len < (8 + offset + 2)) 28209edbd4a0SFrançois Tigeot goto out; 28219edbd4a0SFrançois Tigeot 28229edbd4a0SFrançois Tigeot /* 3D_Present */ 28239edbd4a0SFrançois Tigeot offset++; 28249edbd4a0SFrançois Tigeot if (db[8 + offset] & (1 << 7)) { 28259edbd4a0SFrançois Tigeot modes += add_hdmi_mandatory_stereo_modes(connector); 28269edbd4a0SFrançois Tigeot 28279edbd4a0SFrançois Tigeot /* 3D_Multi_present */ 28289edbd4a0SFrançois Tigeot multi_present = (db[8 + offset] & 0x60) >> 5; 28299edbd4a0SFrançois Tigeot } 28309edbd4a0SFrançois Tigeot 28319edbd4a0SFrançois Tigeot offset++; 28329edbd4a0SFrançois Tigeot vic_len = db[8 + offset] >> 5; 28339edbd4a0SFrançois Tigeot hdmi_3d_len = db[8 + offset] & 0x1f; 28349edbd4a0SFrançois Tigeot 28359edbd4a0SFrançois Tigeot for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { 28369edbd4a0SFrançois Tigeot u8 vic; 28379edbd4a0SFrançois Tigeot 28389edbd4a0SFrançois Tigeot vic = db[9 + offset + i]; 28399edbd4a0SFrançois Tigeot modes += add_hdmi_mode(connector, vic); 28409edbd4a0SFrançois Tigeot } 28419edbd4a0SFrançois Tigeot offset += 1 + vic_len; 28429edbd4a0SFrançois Tigeot 28439edbd4a0SFrançois Tigeot if (multi_present == 1) 28449edbd4a0SFrançois Tigeot multi_len = 2; 28459edbd4a0SFrançois Tigeot else if (multi_present == 2) 28469edbd4a0SFrançois Tigeot multi_len = 4; 28479edbd4a0SFrançois Tigeot else 28489edbd4a0SFrançois Tigeot multi_len = 0; 28499edbd4a0SFrançois Tigeot 28509edbd4a0SFrançois Tigeot if (len < (8 + offset + hdmi_3d_len - 1)) 28519edbd4a0SFrançois Tigeot goto out; 28529edbd4a0SFrançois Tigeot 28539edbd4a0SFrançois Tigeot if (hdmi_3d_len < multi_len) 28549edbd4a0SFrançois Tigeot goto out; 28559edbd4a0SFrançois Tigeot 28569edbd4a0SFrançois Tigeot if (multi_present == 1 || multi_present == 2) { 28579edbd4a0SFrançois Tigeot /* 3D_Structure_ALL */ 28589edbd4a0SFrançois Tigeot structure_all = (db[8 + offset] << 8) | db[9 + offset]; 28599edbd4a0SFrançois Tigeot 28609edbd4a0SFrançois Tigeot /* check if 3D_MASK is present */ 28619edbd4a0SFrançois Tigeot if (multi_present == 2) 28629edbd4a0SFrançois Tigeot mask = (db[10 + offset] << 8) | db[11 + offset]; 28639edbd4a0SFrançois Tigeot else 28649edbd4a0SFrançois Tigeot mask = 0xffff; 28659edbd4a0SFrançois Tigeot 28669edbd4a0SFrançois Tigeot for (i = 0; i < 16; i++) { 28679edbd4a0SFrançois Tigeot if (mask & (1 << i)) 28689edbd4a0SFrançois Tigeot modes += add_3d_struct_modes(connector, 28699edbd4a0SFrançois Tigeot structure_all, 28709edbd4a0SFrançois Tigeot video_db, 28719edbd4a0SFrançois Tigeot video_len, i); 28729edbd4a0SFrançois Tigeot } 28739edbd4a0SFrançois Tigeot } 28749edbd4a0SFrançois Tigeot 28759edbd4a0SFrançois Tigeot offset += multi_len; 28769edbd4a0SFrançois Tigeot 28779edbd4a0SFrançois Tigeot for (i = 0; i < (hdmi_3d_len - multi_len); i++) { 28789edbd4a0SFrançois Tigeot int vic_index; 28799edbd4a0SFrançois Tigeot struct drm_display_mode *newmode = NULL; 28809edbd4a0SFrançois Tigeot unsigned int newflag = 0; 28819edbd4a0SFrançois Tigeot bool detail_present; 28829edbd4a0SFrançois Tigeot 28839edbd4a0SFrançois Tigeot detail_present = ((db[8 + offset + i] & 0x0f) > 7); 28849edbd4a0SFrançois Tigeot 28859edbd4a0SFrançois Tigeot if (detail_present && (i + 1 == hdmi_3d_len - multi_len)) 28869edbd4a0SFrançois Tigeot break; 28879edbd4a0SFrançois Tigeot 28889edbd4a0SFrançois Tigeot /* 2D_VIC_order_X */ 28899edbd4a0SFrançois Tigeot vic_index = db[8 + offset + i] >> 4; 28909edbd4a0SFrançois Tigeot 28919edbd4a0SFrançois Tigeot /* 3D_Structure_X */ 28929edbd4a0SFrançois Tigeot switch (db[8 + offset + i] & 0x0f) { 28939edbd4a0SFrançois Tigeot case 0: 28949edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_FRAME_PACKING; 28959edbd4a0SFrançois Tigeot break; 28969edbd4a0SFrançois Tigeot case 6: 28979edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 28989edbd4a0SFrançois Tigeot break; 28999edbd4a0SFrançois Tigeot case 8: 29009edbd4a0SFrançois Tigeot /* 3D_Detail_X */ 29019edbd4a0SFrançois Tigeot if ((db[9 + offset + i] >> 4) == 1) 29029edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 29039edbd4a0SFrançois Tigeot break; 29049edbd4a0SFrançois Tigeot } 29059edbd4a0SFrançois Tigeot 29069edbd4a0SFrançois Tigeot if (newflag != 0) { 29079edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, 29089edbd4a0SFrançois Tigeot video_db, 29099edbd4a0SFrançois Tigeot video_len, 29109edbd4a0SFrançois Tigeot vic_index); 29119edbd4a0SFrançois Tigeot 29129edbd4a0SFrançois Tigeot if (newmode) { 29139edbd4a0SFrançois Tigeot newmode->flags |= newflag; 29149edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 29159edbd4a0SFrançois Tigeot modes++; 29169edbd4a0SFrançois Tigeot } 29179edbd4a0SFrançois Tigeot } 29189edbd4a0SFrançois Tigeot 29199edbd4a0SFrançois Tigeot if (detail_present) 29209edbd4a0SFrançois Tigeot i++; 29219edbd4a0SFrançois Tigeot } 29229edbd4a0SFrançois Tigeot 29239edbd4a0SFrançois Tigeot out: 29249edbd4a0SFrançois Tigeot return modes; 29259edbd4a0SFrançois Tigeot } 29269edbd4a0SFrançois Tigeot 29276e29dde8SFrançois Tigeot static int 29286e29dde8SFrançois Tigeot cea_db_payload_len(const u8 *db) 29296e29dde8SFrançois Tigeot { 29306e29dde8SFrançois Tigeot return db[0] & 0x1f; 29316e29dde8SFrançois Tigeot } 29326e29dde8SFrançois Tigeot 29336e29dde8SFrançois Tigeot static int 29346e29dde8SFrançois Tigeot cea_db_tag(const u8 *db) 29356e29dde8SFrançois Tigeot { 29366e29dde8SFrançois Tigeot return db[0] >> 5; 29376e29dde8SFrançois Tigeot } 29386e29dde8SFrançois Tigeot 29396e29dde8SFrançois Tigeot static int 29406e29dde8SFrançois Tigeot cea_revision(const u8 *cea) 29416e29dde8SFrançois Tigeot { 29426e29dde8SFrançois Tigeot return cea[1]; 29436e29dde8SFrançois Tigeot } 29446e29dde8SFrançois Tigeot 29456e29dde8SFrançois Tigeot static int 29466e29dde8SFrançois Tigeot cea_db_offsets(const u8 *cea, int *start, int *end) 29476e29dde8SFrançois Tigeot { 29486e29dde8SFrançois Tigeot /* Data block offset in CEA extension block */ 29496e29dde8SFrançois Tigeot *start = 4; 29506e29dde8SFrançois Tigeot *end = cea[2]; 29516e29dde8SFrançois Tigeot if (*end == 0) 29526e29dde8SFrançois Tigeot *end = 127; 29536e29dde8SFrançois Tigeot if (*end < 4 || *end > 127) 29546e29dde8SFrançois Tigeot return -ERANGE; 29556e29dde8SFrançois Tigeot return 0; 29566e29dde8SFrançois Tigeot } 29576e29dde8SFrançois Tigeot 29589edbd4a0SFrançois Tigeot static bool cea_db_is_hdmi_vsdb(const u8 *db) 29599edbd4a0SFrançois Tigeot { 29609edbd4a0SFrançois Tigeot int hdmi_id; 29619edbd4a0SFrançois Tigeot 29629edbd4a0SFrançois Tigeot if (cea_db_tag(db) != VENDOR_BLOCK) 29639edbd4a0SFrançois Tigeot return false; 29649edbd4a0SFrançois Tigeot 29659edbd4a0SFrançois Tigeot if (cea_db_payload_len(db) < 5) 29669edbd4a0SFrançois Tigeot return false; 29679edbd4a0SFrançois Tigeot 29689edbd4a0SFrançois Tigeot hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); 29699edbd4a0SFrançois Tigeot 29709edbd4a0SFrançois Tigeot return hdmi_id == HDMI_IEEE_OUI; 29719edbd4a0SFrançois Tigeot } 29729edbd4a0SFrançois Tigeot 29736e29dde8SFrançois Tigeot #define for_each_cea_db(cea, i, start, end) \ 29746e29dde8SFrançois Tigeot for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) 29756e29dde8SFrançois Tigeot 29766e29dde8SFrançois Tigeot static int 29776e29dde8SFrançois Tigeot add_cea_modes(struct drm_connector *connector, struct edid *edid) 29786e29dde8SFrançois Tigeot { 29799edbd4a0SFrançois Tigeot const u8 *cea = drm_find_cea_extension(edid); 29809edbd4a0SFrançois Tigeot const u8 *db, *hdmi = NULL, *video = NULL; 29819edbd4a0SFrançois Tigeot u8 dbl, hdmi_len, video_len = 0; 29826e29dde8SFrançois Tigeot int modes = 0; 29836e29dde8SFrançois Tigeot 29846e29dde8SFrançois Tigeot if (cea && cea_revision(cea) >= 3) { 29856e29dde8SFrançois Tigeot int i, start, end; 29866e29dde8SFrançois Tigeot 29876e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) 29886e29dde8SFrançois Tigeot return 0; 29896e29dde8SFrançois Tigeot 29906e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 29916e29dde8SFrançois Tigeot db = &cea[i]; 29926e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 29936e29dde8SFrançois Tigeot 29949edbd4a0SFrançois Tigeot if (cea_db_tag(db) == VIDEO_BLOCK) { 29959edbd4a0SFrançois Tigeot video = db + 1; 29969edbd4a0SFrançois Tigeot video_len = dbl; 29979edbd4a0SFrançois Tigeot modes += do_cea_modes(connector, video, dbl); 29989edbd4a0SFrançois Tigeot } 29999edbd4a0SFrançois Tigeot else if (cea_db_is_hdmi_vsdb(db)) { 30009edbd4a0SFrançois Tigeot hdmi = db; 30019edbd4a0SFrançois Tigeot hdmi_len = dbl; 30026e29dde8SFrançois Tigeot } 30036e29dde8SFrançois Tigeot } 30049edbd4a0SFrançois Tigeot } 30059edbd4a0SFrançois Tigeot 30069edbd4a0SFrançois Tigeot /* 30079edbd4a0SFrançois Tigeot * We parse the HDMI VSDB after having added the cea modes as we will 30089edbd4a0SFrançois Tigeot * be patching their flags when the sink supports stereo 3D. 30099edbd4a0SFrançois Tigeot */ 30109edbd4a0SFrançois Tigeot if (hdmi) 30119edbd4a0SFrançois Tigeot modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, 30129edbd4a0SFrançois Tigeot video_len); 30136e29dde8SFrançois Tigeot 30146e29dde8SFrançois Tigeot return modes; 30156e29dde8SFrançois Tigeot } 30166e29dde8SFrançois Tigeot 30176e29dde8SFrançois Tigeot static void 30186e29dde8SFrançois Tigeot parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) 30196e29dde8SFrançois Tigeot { 30206e29dde8SFrançois Tigeot u8 len = cea_db_payload_len(db); 30216e29dde8SFrançois Tigeot 30226e29dde8SFrançois Tigeot if (len >= 6) { 30235718399fSFrançois Tigeot connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ 30245718399fSFrançois Tigeot connector->dvi_dual = db[6] & 1; 30256e29dde8SFrançois Tigeot } 30266e29dde8SFrançois Tigeot if (len >= 7) 30275718399fSFrançois Tigeot connector->max_tmds_clock = db[7] * 5; 30286e29dde8SFrançois Tigeot if (len >= 8) { 30295718399fSFrançois Tigeot connector->latency_present[0] = db[8] >> 7; 30305718399fSFrançois Tigeot connector->latency_present[1] = (db[8] >> 6) & 1; 30316e29dde8SFrançois Tigeot } 30326e29dde8SFrançois Tigeot if (len >= 9) 30335718399fSFrançois Tigeot connector->video_latency[0] = db[9]; 30346e29dde8SFrançois Tigeot if (len >= 10) 30355718399fSFrançois Tigeot connector->audio_latency[0] = db[10]; 30366e29dde8SFrançois Tigeot if (len >= 11) 30375718399fSFrançois Tigeot connector->video_latency[1] = db[11]; 30386e29dde8SFrançois Tigeot if (len >= 12) 30395718399fSFrançois Tigeot connector->audio_latency[1] = db[12]; 30405718399fSFrançois Tigeot 30415718399fSFrançois Tigeot DRM_DEBUG_KMS("HDMI: DVI dual %d, " 30425718399fSFrançois Tigeot "max TMDS clock %d, " 30435718399fSFrançois Tigeot "latency present %d %d, " 30445718399fSFrançois Tigeot "video latency %d %d, " 30455718399fSFrançois Tigeot "audio latency %d %d\n", 30465718399fSFrançois Tigeot connector->dvi_dual, 30475718399fSFrançois Tigeot connector->max_tmds_clock, 30485718399fSFrançois Tigeot (int) connector->latency_present[0], 30495718399fSFrançois Tigeot (int) connector->latency_present[1], 30505718399fSFrançois Tigeot connector->video_latency[0], 30515718399fSFrançois Tigeot connector->video_latency[1], 30525718399fSFrançois Tigeot connector->audio_latency[0], 30535718399fSFrançois Tigeot connector->audio_latency[1]); 30545718399fSFrançois Tigeot } 30555718399fSFrançois Tigeot 30565718399fSFrançois Tigeot static void 30575718399fSFrançois Tigeot monitor_name(struct detailed_timing *t, void *data) 30585718399fSFrançois Tigeot { 30595718399fSFrançois Tigeot if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME) 30605718399fSFrançois Tigeot *(u8 **)data = t->data.other_data.data.str.str; 30615718399fSFrançois Tigeot } 30625718399fSFrançois Tigeot 30635718399fSFrançois Tigeot /** 30645718399fSFrançois Tigeot * drm_edid_to_eld - build ELD from EDID 30655718399fSFrançois Tigeot * @connector: connector corresponding to the HDMI/DP sink 30665718399fSFrançois Tigeot * @edid: EDID to parse 30675718399fSFrançois Tigeot * 3068ba55f2f5SFrançois Tigeot * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The 3069ba55f2f5SFrançois Tigeot * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to 3070ba55f2f5SFrançois Tigeot * fill in. 30715718399fSFrançois Tigeot */ 30725718399fSFrançois Tigeot void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) 30735718399fSFrançois Tigeot { 30745718399fSFrançois Tigeot uint8_t *eld = connector->eld; 30755718399fSFrançois Tigeot u8 *cea; 30765718399fSFrançois Tigeot u8 *name; 30775718399fSFrançois Tigeot u8 *db; 30785718399fSFrançois Tigeot int sad_count = 0; 30795718399fSFrançois Tigeot int mnl; 30805718399fSFrançois Tigeot int dbl; 30815718399fSFrançois Tigeot 30825718399fSFrançois Tigeot memset(eld, 0, sizeof(connector->eld)); 30835718399fSFrançois Tigeot 30845718399fSFrançois Tigeot cea = drm_find_cea_extension(edid); 30855718399fSFrançois Tigeot if (!cea) { 30865718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD: no CEA Extension found\n"); 30875718399fSFrançois Tigeot return; 30885718399fSFrançois Tigeot } 30895718399fSFrançois Tigeot 30905718399fSFrançois Tigeot name = NULL; 30915718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, monitor_name, &name); 30925718399fSFrançois Tigeot for (mnl = 0; name && mnl < 13; mnl++) { 30935718399fSFrançois Tigeot if (name[mnl] == 0x0a) 30945718399fSFrançois Tigeot break; 30955718399fSFrançois Tigeot eld[20 + mnl] = name[mnl]; 30965718399fSFrançois Tigeot } 30975718399fSFrançois Tigeot eld[4] = (cea[1] << 5) | mnl; 30985718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20); 30995718399fSFrançois Tigeot 31005718399fSFrançois Tigeot eld[0] = 2 << 3; /* ELD version: 2 */ 31015718399fSFrançois Tigeot 31025718399fSFrançois Tigeot eld[16] = edid->mfg_id[0]; 31035718399fSFrançois Tigeot eld[17] = edid->mfg_id[1]; 31045718399fSFrançois Tigeot eld[18] = edid->prod_code[0]; 31055718399fSFrançois Tigeot eld[19] = edid->prod_code[1]; 31065718399fSFrançois Tigeot 31076e29dde8SFrançois Tigeot if (cea_revision(cea) >= 3) { 31086e29dde8SFrançois Tigeot int i, start, end; 31095718399fSFrançois Tigeot 31106e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 31116e29dde8SFrançois Tigeot start = 0; 31126e29dde8SFrançois Tigeot end = 0; 31136e29dde8SFrançois Tigeot } 31146e29dde8SFrançois Tigeot 31156e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 31166e29dde8SFrançois Tigeot db = &cea[i]; 31176e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 31186e29dde8SFrançois Tigeot 31196e29dde8SFrançois Tigeot switch (cea_db_tag(db)) { 31206e29dde8SFrançois Tigeot case AUDIO_BLOCK: 31216e29dde8SFrançois Tigeot /* Audio Data Block, contains SADs */ 31225718399fSFrançois Tigeot sad_count = dbl / 3; 31236e29dde8SFrançois Tigeot if (dbl >= 1) 31245718399fSFrançois Tigeot memcpy(eld + 20 + mnl, &db[1], dbl); 31255718399fSFrançois Tigeot break; 31266e29dde8SFrançois Tigeot case SPEAKER_BLOCK: 31276e29dde8SFrançois Tigeot /* Speaker Allocation Data Block */ 31286e29dde8SFrançois Tigeot if (dbl >= 1) 31295718399fSFrançois Tigeot eld[7] = db[1]; 31305718399fSFrançois Tigeot break; 31315718399fSFrançois Tigeot case VENDOR_BLOCK: 31325718399fSFrançois Tigeot /* HDMI Vendor-Specific Data Block */ 31336e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(db)) 31345718399fSFrançois Tigeot parse_hdmi_vsdb(connector, db); 31355718399fSFrançois Tigeot break; 31365718399fSFrançois Tigeot default: 31375718399fSFrançois Tigeot break; 31385718399fSFrançois Tigeot } 31395718399fSFrançois Tigeot } 31406e29dde8SFrançois Tigeot } 31415718399fSFrançois Tigeot eld[5] |= sad_count << 4; 31425718399fSFrançois Tigeot eld[2] = (20 + mnl + sad_count * 3 + 3) / 4; 31435718399fSFrançois Tigeot 31445718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count); 31455718399fSFrançois Tigeot } 31466e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_eld); 31475718399fSFrançois Tigeot 31485718399fSFrançois Tigeot /** 31499edbd4a0SFrançois Tigeot * drm_edid_to_sad - extracts SADs from EDID 31509edbd4a0SFrançois Tigeot * @edid: EDID to parse 31519edbd4a0SFrançois Tigeot * @sads: pointer that will be set to the extracted SADs 31529edbd4a0SFrançois Tigeot * 31539edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. 31549edbd4a0SFrançois Tigeot * 3155ba55f2f5SFrançois Tigeot * Note: The returned pointer needs to be freed using kfree(). 3156ba55f2f5SFrançois Tigeot * 3157ba55f2f5SFrançois Tigeot * Return: The number of found SADs or negative number on error. 31589edbd4a0SFrançois Tigeot */ 31599edbd4a0SFrançois Tigeot int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) 31609edbd4a0SFrançois Tigeot { 31619edbd4a0SFrançois Tigeot int count = 0; 31629edbd4a0SFrançois Tigeot int i, start, end, dbl; 31639edbd4a0SFrançois Tigeot u8 *cea; 31649edbd4a0SFrançois Tigeot 31659edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 31669edbd4a0SFrançois Tigeot if (!cea) { 31679edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 31689edbd4a0SFrançois Tigeot return -ENOENT; 31699edbd4a0SFrançois Tigeot } 31709edbd4a0SFrançois Tigeot 31719edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 31729edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 3173f43cf1b1SMichael Neumann return -EOPNOTSUPP; 31749edbd4a0SFrançois Tigeot } 31759edbd4a0SFrançois Tigeot 31769edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 31779edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 31789edbd4a0SFrançois Tigeot return -EPROTO; 31799edbd4a0SFrançois Tigeot } 31809edbd4a0SFrançois Tigeot 31819edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 31829edbd4a0SFrançois Tigeot u8 *db = &cea[i]; 31839edbd4a0SFrançois Tigeot 31849edbd4a0SFrançois Tigeot if (cea_db_tag(db) == AUDIO_BLOCK) { 31859edbd4a0SFrançois Tigeot int j; 31869edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 31879edbd4a0SFrançois Tigeot 31889edbd4a0SFrançois Tigeot count = dbl / 3; /* SAD is 3B */ 318922ee5efbSFrançois Tigeot *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); 31909edbd4a0SFrançois Tigeot if (!*sads) 31919edbd4a0SFrançois Tigeot return -ENOMEM; 31929edbd4a0SFrançois Tigeot for (j = 0; j < count; j++) { 31939edbd4a0SFrançois Tigeot u8 *sad = &db[1 + j * 3]; 31949edbd4a0SFrançois Tigeot 31959edbd4a0SFrançois Tigeot (*sads)[j].format = (sad[0] & 0x78) >> 3; 31969edbd4a0SFrançois Tigeot (*sads)[j].channels = sad[0] & 0x7; 31979edbd4a0SFrançois Tigeot (*sads)[j].freq = sad[1] & 0x7F; 31989edbd4a0SFrançois Tigeot (*sads)[j].byte2 = sad[2]; 31999edbd4a0SFrançois Tigeot } 32009edbd4a0SFrançois Tigeot break; 32019edbd4a0SFrançois Tigeot } 32029edbd4a0SFrançois Tigeot } 32039edbd4a0SFrançois Tigeot 32049edbd4a0SFrançois Tigeot return count; 32059edbd4a0SFrançois Tigeot } 32069edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_sad); 32079edbd4a0SFrançois Tigeot 32089edbd4a0SFrançois Tigeot /** 32099edbd4a0SFrançois Tigeot * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID 32109edbd4a0SFrançois Tigeot * @edid: EDID to parse 32119edbd4a0SFrançois Tigeot * @sadb: pointer to the speaker block 32129edbd4a0SFrançois Tigeot * 32139edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. 32149edbd4a0SFrançois Tigeot * 3215ba55f2f5SFrançois Tigeot * Note: The returned pointer needs to be freed using kfree(). 3216ba55f2f5SFrançois Tigeot * 3217ba55f2f5SFrançois Tigeot * Return: The number of found Speaker Allocation Blocks or negative number on 3218ba55f2f5SFrançois Tigeot * error. 32199edbd4a0SFrançois Tigeot */ 32209edbd4a0SFrançois Tigeot int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) 32219edbd4a0SFrançois Tigeot { 32229edbd4a0SFrançois Tigeot int count = 0; 32239edbd4a0SFrançois Tigeot int i, start, end, dbl; 32249edbd4a0SFrançois Tigeot const u8 *cea; 32259edbd4a0SFrançois Tigeot 32269edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 32279edbd4a0SFrançois Tigeot if (!cea) { 32289edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 32299edbd4a0SFrançois Tigeot return -ENOENT; 32309edbd4a0SFrançois Tigeot } 32319edbd4a0SFrançois Tigeot 32329edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 32339edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 32349edbd4a0SFrançois Tigeot return -ENOTSUPP; 32359edbd4a0SFrançois Tigeot } 32369edbd4a0SFrançois Tigeot 32379edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 32389edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 32399edbd4a0SFrançois Tigeot return -EPROTO; 32409edbd4a0SFrançois Tigeot } 32419edbd4a0SFrançois Tigeot 32429edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 32439edbd4a0SFrançois Tigeot const u8 *db = &cea[i]; 32449edbd4a0SFrançois Tigeot 32459edbd4a0SFrançois Tigeot if (cea_db_tag(db) == SPEAKER_BLOCK) { 32469edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 32479edbd4a0SFrançois Tigeot 32489edbd4a0SFrançois Tigeot /* Speaker Allocation Data Block */ 32499edbd4a0SFrançois Tigeot if (dbl == 3) { 3250ba55f2f5SFrançois Tigeot *sadb = kmemdup(&db[1], dbl, GFP_KERNEL); 3251ba55f2f5SFrançois Tigeot if (!*sadb) 3252ba55f2f5SFrançois Tigeot return -ENOMEM; 32539edbd4a0SFrançois Tigeot count = dbl; 32549edbd4a0SFrançois Tigeot break; 32559edbd4a0SFrançois Tigeot } 32569edbd4a0SFrançois Tigeot } 32579edbd4a0SFrançois Tigeot } 32589edbd4a0SFrançois Tigeot 32599edbd4a0SFrançois Tigeot return count; 32609edbd4a0SFrançois Tigeot } 32619edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_speaker_allocation); 32629edbd4a0SFrançois Tigeot 32639edbd4a0SFrançois Tigeot /** 3264ba55f2f5SFrançois Tigeot * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay 32655718399fSFrançois Tigeot * @connector: connector associated with the HDMI/DP sink 32665718399fSFrançois Tigeot * @mode: the display mode 3267ba55f2f5SFrançois Tigeot * 3268ba55f2f5SFrançois Tigeot * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if 3269ba55f2f5SFrançois Tigeot * the sink doesn't support audio or video. 32705718399fSFrançois Tigeot */ 32715718399fSFrançois Tigeot int drm_av_sync_delay(struct drm_connector *connector, 32725718399fSFrançois Tigeot struct drm_display_mode *mode) 32735718399fSFrançois Tigeot { 32745718399fSFrançois Tigeot int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 32755718399fSFrançois Tigeot int a, v; 32765718399fSFrançois Tigeot 32775718399fSFrançois Tigeot if (!connector->latency_present[0]) 32785718399fSFrançois Tigeot return 0; 32795718399fSFrançois Tigeot if (!connector->latency_present[1]) 32805718399fSFrançois Tigeot i = 0; 32815718399fSFrançois Tigeot 32825718399fSFrançois Tigeot a = connector->audio_latency[i]; 32835718399fSFrançois Tigeot v = connector->video_latency[i]; 32845718399fSFrançois Tigeot 32855718399fSFrançois Tigeot /* 32865718399fSFrançois Tigeot * HDMI/DP sink doesn't support audio or video? 32875718399fSFrançois Tigeot */ 32885718399fSFrançois Tigeot if (a == 255 || v == 255) 32895718399fSFrançois Tigeot return 0; 32905718399fSFrançois Tigeot 32915718399fSFrançois Tigeot /* 32925718399fSFrançois Tigeot * Convert raw EDID values to millisecond. 32935718399fSFrançois Tigeot * Treat unknown latency as 0ms. 32945718399fSFrançois Tigeot */ 32955718399fSFrançois Tigeot if (a) 32965718399fSFrançois Tigeot a = min(2 * (a - 1), 500); 32975718399fSFrançois Tigeot if (v) 32985718399fSFrançois Tigeot v = min(2 * (v - 1), 500); 32995718399fSFrançois Tigeot 33005718399fSFrançois Tigeot return max(v - a, 0); 33015718399fSFrançois Tigeot } 33026e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_av_sync_delay); 33035718399fSFrançois Tigeot 33045718399fSFrançois Tigeot /** 33055718399fSFrançois Tigeot * drm_select_eld - select one ELD from multiple HDMI/DP sinks 33065718399fSFrançois Tigeot * @encoder: the encoder just changed display mode 33075718399fSFrançois Tigeot * @mode: the adjusted display mode 33085718399fSFrançois Tigeot * 33095718399fSFrançois Tigeot * It's possible for one encoder to be associated with multiple HDMI/DP sinks. 33105718399fSFrançois Tigeot * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. 3311ba55f2f5SFrançois Tigeot * 3312ba55f2f5SFrançois Tigeot * Return: The connector associated with the first HDMI/DP sink that has ELD 3313ba55f2f5SFrançois Tigeot * attached to it. 33145718399fSFrançois Tigeot */ 33155718399fSFrançois Tigeot struct drm_connector *drm_select_eld(struct drm_encoder *encoder, 33165718399fSFrançois Tigeot struct drm_display_mode *mode) 33175718399fSFrançois Tigeot { 33185718399fSFrançois Tigeot struct drm_connector *connector; 33195718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 33205718399fSFrançois Tigeot 3321ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 3322*24edb884SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 3323ba55f2f5SFrançois Tigeot 33245718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 33255718399fSFrançois Tigeot if (connector->encoder == encoder && connector->eld[0]) 33265718399fSFrançois Tigeot return connector; 33275718399fSFrançois Tigeot 33285718399fSFrançois Tigeot return NULL; 33295718399fSFrançois Tigeot } 33306e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_select_eld); 33315718399fSFrançois Tigeot 33325718399fSFrançois Tigeot /** 3333ba55f2f5SFrançois Tigeot * drm_detect_hdmi_monitor - detect whether monitor is HDMI 33345718399fSFrançois Tigeot * @edid: monitor EDID information 33355718399fSFrançois Tigeot * 33365718399fSFrançois Tigeot * Parse the CEA extension according to CEA-861-B. 3337ba55f2f5SFrançois Tigeot * 3338ba55f2f5SFrançois Tigeot * Return: True if the monitor is HDMI, false if not or unknown. 33395718399fSFrançois Tigeot */ 33405718399fSFrançois Tigeot bool drm_detect_hdmi_monitor(struct edid *edid) 33415718399fSFrançois Tigeot { 33425718399fSFrançois Tigeot u8 *edid_ext; 33436e29dde8SFrançois Tigeot int i; 33445718399fSFrançois Tigeot int start_offset, end_offset; 33455718399fSFrançois Tigeot 33465718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 33475718399fSFrançois Tigeot if (!edid_ext) 33486e29dde8SFrançois Tigeot return false; 33495718399fSFrançois Tigeot 33506e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 33516e29dde8SFrançois Tigeot return false; 33525718399fSFrançois Tigeot 33535718399fSFrançois Tigeot /* 33545718399fSFrançois Tigeot * Because HDMI identifier is in Vendor Specific Block, 33555718399fSFrançois Tigeot * search it from all data blocks of CEA extension. 33565718399fSFrançois Tigeot */ 33576e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 33586e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(&edid_ext[i])) 33596e29dde8SFrançois Tigeot return true; 33605718399fSFrançois Tigeot } 33615718399fSFrançois Tigeot 33626e29dde8SFrançois Tigeot return false; 33635718399fSFrançois Tigeot } 33646e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_hdmi_monitor); 33655718399fSFrançois Tigeot 33665718399fSFrançois Tigeot /** 33675718399fSFrançois Tigeot * drm_detect_monitor_audio - check monitor audio capability 3368ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 33695718399fSFrançois Tigeot * 33705718399fSFrançois Tigeot * Monitor should have CEA extension block. 33715718399fSFrançois Tigeot * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic 33725718399fSFrançois Tigeot * audio' only. If there is any audio extension block and supported 33735718399fSFrançois Tigeot * audio format, assume at least 'basic audio' support, even if 'basic 33745718399fSFrançois Tigeot * audio' is not defined in EDID. 33755718399fSFrançois Tigeot * 3376ba55f2f5SFrançois Tigeot * Return: True if the monitor supports audio, false otherwise. 33775718399fSFrançois Tigeot */ 33785718399fSFrançois Tigeot bool drm_detect_monitor_audio(struct edid *edid) 33795718399fSFrançois Tigeot { 33805718399fSFrançois Tigeot u8 *edid_ext; 33815718399fSFrançois Tigeot int i, j; 33825718399fSFrançois Tigeot bool has_audio = false; 33835718399fSFrançois Tigeot int start_offset, end_offset; 33845718399fSFrançois Tigeot 33855718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 33865718399fSFrançois Tigeot if (!edid_ext) 33875718399fSFrançois Tigeot goto end; 33885718399fSFrançois Tigeot 33895718399fSFrançois Tigeot has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0); 33905718399fSFrançois Tigeot 33915718399fSFrançois Tigeot if (has_audio) { 33925718399fSFrançois Tigeot DRM_DEBUG_KMS("Monitor has basic audio support\n"); 33935718399fSFrançois Tigeot goto end; 33945718399fSFrançois Tigeot } 33955718399fSFrançois Tigeot 33966e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 33976e29dde8SFrançois Tigeot goto end; 33985718399fSFrançois Tigeot 33996e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 34006e29dde8SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { 34015718399fSFrançois Tigeot has_audio = true; 34026e29dde8SFrançois Tigeot for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) 34035718399fSFrançois Tigeot DRM_DEBUG_KMS("CEA audio format %d\n", 34045718399fSFrançois Tigeot (edid_ext[i + j] >> 3) & 0xf); 34055718399fSFrançois Tigeot goto end; 34065718399fSFrançois Tigeot } 34075718399fSFrançois Tigeot } 34085718399fSFrançois Tigeot end: 34095718399fSFrançois Tigeot return has_audio; 34105718399fSFrançois Tigeot } 34116e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_monitor_audio); 34125718399fSFrançois Tigeot 34135718399fSFrançois Tigeot /** 3414a2fdbec6SFrançois Tigeot * drm_rgb_quant_range_selectable - is RGB quantization range selectable? 3415ba55f2f5SFrançois Tigeot * @edid: EDID block to scan 3416a2fdbec6SFrançois Tigeot * 3417a2fdbec6SFrançois Tigeot * Check whether the monitor reports the RGB quantization range selection 3418a2fdbec6SFrançois Tigeot * as supported. The AVI infoframe can then be used to inform the monitor 3419a2fdbec6SFrançois Tigeot * which quantization range (full or limited) is used. 3420ba55f2f5SFrançois Tigeot * 3421ba55f2f5SFrançois Tigeot * Return: True if the RGB quantization range is selectable, false otherwise. 3422a2fdbec6SFrançois Tigeot */ 3423a2fdbec6SFrançois Tigeot bool drm_rgb_quant_range_selectable(struct edid *edid) 3424a2fdbec6SFrançois Tigeot { 3425a2fdbec6SFrançois Tigeot u8 *edid_ext; 3426a2fdbec6SFrançois Tigeot int i, start, end; 3427a2fdbec6SFrançois Tigeot 3428a2fdbec6SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 3429a2fdbec6SFrançois Tigeot if (!edid_ext) 3430a2fdbec6SFrançois Tigeot return false; 3431a2fdbec6SFrançois Tigeot 3432a2fdbec6SFrançois Tigeot if (cea_db_offsets(edid_ext, &start, &end)) 3433a2fdbec6SFrançois Tigeot return false; 3434a2fdbec6SFrançois Tigeot 3435a2fdbec6SFrançois Tigeot for_each_cea_db(edid_ext, i, start, end) { 3436a2fdbec6SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && 3437a2fdbec6SFrançois Tigeot cea_db_payload_len(&edid_ext[i]) == 2) { 3438a2fdbec6SFrançois Tigeot DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); 3439a2fdbec6SFrançois Tigeot return edid_ext[i + 2] & EDID_CEA_VCDB_QS; 3440a2fdbec6SFrançois Tigeot } 3441a2fdbec6SFrançois Tigeot } 3442a2fdbec6SFrançois Tigeot 3443a2fdbec6SFrançois Tigeot return false; 3444a2fdbec6SFrançois Tigeot } 3445a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_rgb_quant_range_selectable); 3446a2fdbec6SFrançois Tigeot 3447a2fdbec6SFrançois Tigeot /** 3448ba55f2f5SFrançois Tigeot * drm_assign_hdmi_deep_color_info - detect whether monitor supports 3449ba55f2f5SFrançois Tigeot * hdmi deep color modes and update drm_display_info if so. 3450ba55f2f5SFrançois Tigeot * 3451ba55f2f5SFrançois Tigeot * @edid: monitor EDID information 3452ba55f2f5SFrançois Tigeot * @info: Updated with maximum supported deep color bpc and color format 3453ba55f2f5SFrançois Tigeot * if deep color supported. 3454ba55f2f5SFrançois Tigeot * 3455ba55f2f5SFrançois Tigeot * Parse the CEA extension according to CEA-861-B. 3456ba55f2f5SFrançois Tigeot * Return true if HDMI deep color supported, false if not or unknown. 3457ba55f2f5SFrançois Tigeot */ 3458ba55f2f5SFrançois Tigeot static bool drm_assign_hdmi_deep_color_info(struct edid *edid, 3459ba55f2f5SFrançois Tigeot struct drm_display_info *info, 3460ba55f2f5SFrançois Tigeot struct drm_connector *connector) 3461ba55f2f5SFrançois Tigeot { 3462ba55f2f5SFrançois Tigeot u8 *edid_ext, *hdmi; 3463ba55f2f5SFrançois Tigeot int i; 3464ba55f2f5SFrançois Tigeot int start_offset, end_offset; 3465ba55f2f5SFrançois Tigeot unsigned int dc_bpc = 0; 3466ba55f2f5SFrançois Tigeot 3467ba55f2f5SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 3468ba55f2f5SFrançois Tigeot if (!edid_ext) 3469ba55f2f5SFrançois Tigeot return false; 3470ba55f2f5SFrançois Tigeot 3471ba55f2f5SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 3472ba55f2f5SFrançois Tigeot return false; 3473ba55f2f5SFrançois Tigeot 3474ba55f2f5SFrançois Tigeot /* 3475ba55f2f5SFrançois Tigeot * Because HDMI identifier is in Vendor Specific Block, 3476ba55f2f5SFrançois Tigeot * search it from all data blocks of CEA extension. 3477ba55f2f5SFrançois Tigeot */ 3478ba55f2f5SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 3479ba55f2f5SFrançois Tigeot if (cea_db_is_hdmi_vsdb(&edid_ext[i])) { 3480ba55f2f5SFrançois Tigeot /* HDMI supports at least 8 bpc */ 3481ba55f2f5SFrançois Tigeot info->bpc = 8; 3482ba55f2f5SFrançois Tigeot 3483ba55f2f5SFrançois Tigeot hdmi = &edid_ext[i]; 3484ba55f2f5SFrançois Tigeot if (cea_db_payload_len(hdmi) < 6) 3485ba55f2f5SFrançois Tigeot return false; 3486ba55f2f5SFrançois Tigeot 3487ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_30) { 3488ba55f2f5SFrançois Tigeot dc_bpc = 10; 3489c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30; 3490ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 30.\n", 3491ba55f2f5SFrançois Tigeot connector->name); 3492ba55f2f5SFrançois Tigeot } 3493ba55f2f5SFrançois Tigeot 3494ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_36) { 3495ba55f2f5SFrançois Tigeot dc_bpc = 12; 3496c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36; 3497ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 36.\n", 3498ba55f2f5SFrançois Tigeot connector->name); 3499ba55f2f5SFrançois Tigeot } 3500ba55f2f5SFrançois Tigeot 3501ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_48) { 3502ba55f2f5SFrançois Tigeot dc_bpc = 16; 3503c6f73aabSFrançois Tigeot info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48; 3504ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does deep color 48.\n", 3505ba55f2f5SFrançois Tigeot connector->name); 3506ba55f2f5SFrançois Tigeot } 3507ba55f2f5SFrançois Tigeot 3508ba55f2f5SFrançois Tigeot if (dc_bpc > 0) { 3509ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n", 3510ba55f2f5SFrançois Tigeot connector->name, dc_bpc); 3511ba55f2f5SFrançois Tigeot info->bpc = dc_bpc; 3512ba55f2f5SFrançois Tigeot 3513ba55f2f5SFrançois Tigeot /* 3514ba55f2f5SFrançois Tigeot * Deep color support mandates RGB444 support for all video 3515ba55f2f5SFrançois Tigeot * modes and forbids YCRCB422 support for all video modes per 3516ba55f2f5SFrançois Tigeot * HDMI 1.3 spec. 3517ba55f2f5SFrançois Tigeot */ 3518ba55f2f5SFrançois Tigeot info->color_formats = DRM_COLOR_FORMAT_RGB444; 3519ba55f2f5SFrançois Tigeot 3520ba55f2f5SFrançois Tigeot /* YCRCB444 is optional according to spec. */ 3521ba55f2f5SFrançois Tigeot if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) { 3522ba55f2f5SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 3523ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n", 3524ba55f2f5SFrançois Tigeot connector->name); 3525ba55f2f5SFrançois Tigeot } 3526ba55f2f5SFrançois Tigeot 3527ba55f2f5SFrançois Tigeot /* 3528ba55f2f5SFrançois Tigeot * Spec says that if any deep color mode is supported at all, 3529ba55f2f5SFrançois Tigeot * then deep color 36 bit must be supported. 3530ba55f2f5SFrançois Tigeot */ 3531ba55f2f5SFrançois Tigeot if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) { 3532ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n", 3533ba55f2f5SFrançois Tigeot connector->name); 3534ba55f2f5SFrançois Tigeot } 3535ba55f2f5SFrançois Tigeot 3536ba55f2f5SFrançois Tigeot return true; 3537ba55f2f5SFrançois Tigeot } 3538ba55f2f5SFrançois Tigeot else { 3539ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: No deep color support on this HDMI sink.\n", 3540ba55f2f5SFrançois Tigeot connector->name); 3541ba55f2f5SFrançois Tigeot } 3542ba55f2f5SFrançois Tigeot } 3543ba55f2f5SFrançois Tigeot } 3544ba55f2f5SFrançois Tigeot 3545ba55f2f5SFrançois Tigeot return false; 3546ba55f2f5SFrançois Tigeot } 3547ba55f2f5SFrançois Tigeot 3548ba55f2f5SFrançois Tigeot /** 35495718399fSFrançois Tigeot * drm_add_display_info - pull display info out if present 35505718399fSFrançois Tigeot * @edid: EDID data 35515718399fSFrançois Tigeot * @info: display info (attached to connector) 3552ba55f2f5SFrançois Tigeot * @connector: connector whose edid is used to build display info 35535718399fSFrançois Tigeot * 35545718399fSFrançois Tigeot * Grab any available display info and stuff it into the drm_display_info 35555718399fSFrançois Tigeot * structure that's part of the connector. Useful for tracking bpp and 35565718399fSFrançois Tigeot * color spaces. 35575718399fSFrançois Tigeot */ 35585718399fSFrançois Tigeot static void drm_add_display_info(struct edid *edid, 3559ba55f2f5SFrançois Tigeot struct drm_display_info *info, 3560ba55f2f5SFrançois Tigeot struct drm_connector *connector) 35615718399fSFrançois Tigeot { 35625718399fSFrançois Tigeot u8 *edid_ext; 35635718399fSFrançois Tigeot 35645718399fSFrançois Tigeot info->width_mm = edid->width_cm * 10; 35655718399fSFrançois Tigeot info->height_mm = edid->height_cm * 10; 35665718399fSFrançois Tigeot 35675718399fSFrançois Tigeot /* driver figures it out in this case */ 35685718399fSFrançois Tigeot info->bpc = 0; 35695718399fSFrançois Tigeot info->color_formats = 0; 35705718399fSFrançois Tigeot 35716e29dde8SFrançois Tigeot if (edid->revision < 3) 35725718399fSFrançois Tigeot return; 35735718399fSFrançois Tigeot 35745718399fSFrançois Tigeot if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) 35755718399fSFrançois Tigeot return; 35765718399fSFrançois Tigeot 35776e29dde8SFrançois Tigeot /* Get data from CEA blocks if present */ 35786e29dde8SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 35796e29dde8SFrançois Tigeot if (edid_ext) { 35806e29dde8SFrançois Tigeot info->cea_rev = edid_ext[1]; 35816e29dde8SFrançois Tigeot 35826e29dde8SFrançois Tigeot /* The existence of a CEA block should imply RGB support */ 35836e29dde8SFrançois Tigeot info->color_formats = DRM_COLOR_FORMAT_RGB444; 35846e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB444) 35856e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 35866e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB422) 35876e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 35886e29dde8SFrançois Tigeot } 35896e29dde8SFrançois Tigeot 3590ba55f2f5SFrançois Tigeot /* HDMI deep color modes supported? Assign to info, if so */ 3591ba55f2f5SFrançois Tigeot drm_assign_hdmi_deep_color_info(edid, info, connector); 3592ba55f2f5SFrançois Tigeot 35936e29dde8SFrançois Tigeot /* Only defined for 1.4 with digital displays */ 35946e29dde8SFrançois Tigeot if (edid->revision < 4) 35956e29dde8SFrançois Tigeot return; 35966e29dde8SFrançois Tigeot 35975718399fSFrançois Tigeot switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { 35985718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_6: 35995718399fSFrançois Tigeot info->bpc = 6; 36005718399fSFrançois Tigeot break; 36015718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_8: 36025718399fSFrançois Tigeot info->bpc = 8; 36035718399fSFrançois Tigeot break; 36045718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_10: 36055718399fSFrançois Tigeot info->bpc = 10; 36065718399fSFrançois Tigeot break; 36075718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_12: 36085718399fSFrançois Tigeot info->bpc = 12; 36095718399fSFrançois Tigeot break; 36105718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_14: 36115718399fSFrançois Tigeot info->bpc = 14; 36125718399fSFrançois Tigeot break; 36135718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_16: 36145718399fSFrançois Tigeot info->bpc = 16; 36155718399fSFrançois Tigeot break; 36165718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_UNDEF: 36175718399fSFrançois Tigeot default: 36185718399fSFrançois Tigeot info->bpc = 0; 36195718399fSFrançois Tigeot break; 36205718399fSFrançois Tigeot } 36215718399fSFrançois Tigeot 3622ba55f2f5SFrançois Tigeot DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n", 3623ba55f2f5SFrançois Tigeot connector->name, info->bpc); 3624ba55f2f5SFrançois Tigeot 36256e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_RGB444; 36266e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444) 36276e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 36286e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) 36296e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 36305718399fSFrançois Tigeot } 36315718399fSFrançois Tigeot 36325718399fSFrançois Tigeot /** 36335718399fSFrançois Tigeot * drm_add_edid_modes - add modes from EDID data, if available 36345718399fSFrançois Tigeot * @connector: connector we're probing 3635ba55f2f5SFrançois Tigeot * @edid: EDID data 36365718399fSFrançois Tigeot * 36375718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. 36385718399fSFrançois Tigeot * 3639ba55f2f5SFrançois Tigeot * Return: The number of modes added or 0 if we couldn't find any. 36405718399fSFrançois Tigeot */ 36415718399fSFrançois Tigeot int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) 36425718399fSFrançois Tigeot { 36435718399fSFrançois Tigeot int num_modes = 0; 36445718399fSFrançois Tigeot u32 quirks; 36455718399fSFrançois Tigeot 36465718399fSFrançois Tigeot if (edid == NULL) { 36475718399fSFrançois Tigeot return 0; 36485718399fSFrançois Tigeot } 36495718399fSFrançois Tigeot if (!drm_edid_is_valid(edid)) { 36506e29dde8SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID invalid.\n", 3651ba55f2f5SFrançois Tigeot connector->name); 36525718399fSFrançois Tigeot return 0; 36535718399fSFrançois Tigeot } 36545718399fSFrançois Tigeot 36555718399fSFrançois Tigeot quirks = edid_get_quirks(edid); 36565718399fSFrançois Tigeot 36575718399fSFrançois Tigeot /* 36585718399fSFrançois Tigeot * EDID spec says modes should be preferred in this order: 36595718399fSFrançois Tigeot * - preferred detailed mode 36605718399fSFrançois Tigeot * - other detailed modes from base block 36615718399fSFrançois Tigeot * - detailed modes from extension blocks 36625718399fSFrançois Tigeot * - CVT 3-byte code modes 36635718399fSFrançois Tigeot * - standard timing codes 36645718399fSFrançois Tigeot * - established timing codes 36655718399fSFrançois Tigeot * - modes inferred from GTF or CVT range information 36665718399fSFrançois Tigeot * 36675718399fSFrançois Tigeot * We get this pretty much right. 36685718399fSFrançois Tigeot * 36695718399fSFrançois Tigeot * XXX order for additional mode types in extension blocks? 36705718399fSFrançois Tigeot */ 36715718399fSFrançois Tigeot num_modes += add_detailed_modes(connector, edid, quirks); 36725718399fSFrançois Tigeot num_modes += add_cvt_modes(connector, edid); 36735718399fSFrançois Tigeot num_modes += add_standard_modes(connector, edid); 36745718399fSFrançois Tigeot num_modes += add_established_modes(connector, edid); 3675d82bf20eSFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) 36765718399fSFrançois Tigeot num_modes += add_inferred_modes(connector, edid); 36776e29dde8SFrançois Tigeot num_modes += add_cea_modes(connector, edid); 36789edbd4a0SFrançois Tigeot num_modes += add_alternate_cea_modes(connector, edid); 36795718399fSFrançois Tigeot 36805718399fSFrançois Tigeot if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) 36815718399fSFrançois Tigeot edid_fixup_preferred(connector, quirks); 36825718399fSFrançois Tigeot 3683ba55f2f5SFrançois Tigeot drm_add_display_info(edid, &connector->display_info, connector); 36845718399fSFrançois Tigeot 36859edbd4a0SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_8BPC) 36869edbd4a0SFrançois Tigeot connector->display_info.bpc = 8; 36879edbd4a0SFrançois Tigeot 3688ba55f2f5SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_12BPC) 3689ba55f2f5SFrançois Tigeot connector->display_info.bpc = 12; 3690ba55f2f5SFrançois Tigeot 36915718399fSFrançois Tigeot return num_modes; 36925718399fSFrançois Tigeot } 36936e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_edid_modes); 36945718399fSFrançois Tigeot 36955718399fSFrançois Tigeot /** 36965718399fSFrançois Tigeot * drm_add_modes_noedid - add modes for the connectors without EDID 36975718399fSFrançois Tigeot * @connector: connector we're probing 36985718399fSFrançois Tigeot * @hdisplay: the horizontal display limit 36995718399fSFrançois Tigeot * @vdisplay: the vertical display limit 37005718399fSFrançois Tigeot * 37015718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. Only when the 37025718399fSFrançois Tigeot * hdisplay/vdisplay is not beyond the given limit, it will be added. 37035718399fSFrançois Tigeot * 3704ba55f2f5SFrançois Tigeot * Return: The number of modes added or 0 if we couldn't find any. 37055718399fSFrançois Tigeot */ 37065718399fSFrançois Tigeot int drm_add_modes_noedid(struct drm_connector *connector, 37075718399fSFrançois Tigeot int hdisplay, int vdisplay) 37085718399fSFrançois Tigeot { 37095718399fSFrançois Tigeot int i, count, num_modes = 0; 37105718399fSFrançois Tigeot struct drm_display_mode *mode; 37115718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 37125718399fSFrançois Tigeot 37135718399fSFrançois Tigeot count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); 37145718399fSFrançois Tigeot if (hdisplay < 0) 37155718399fSFrançois Tigeot hdisplay = 0; 37165718399fSFrançois Tigeot if (vdisplay < 0) 37175718399fSFrançois Tigeot vdisplay = 0; 37185718399fSFrançois Tigeot 37195718399fSFrançois Tigeot for (i = 0; i < count; i++) { 3720ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 37215718399fSFrançois Tigeot if (hdisplay && vdisplay) { 37225718399fSFrançois Tigeot /* 37235718399fSFrançois Tigeot * Only when two are valid, they will be used to check 37245718399fSFrançois Tigeot * whether the mode should be added to the mode list of 37255718399fSFrançois Tigeot * the connector. 37265718399fSFrançois Tigeot */ 37275718399fSFrançois Tigeot if (ptr->hdisplay > hdisplay || 37285718399fSFrançois Tigeot ptr->vdisplay > vdisplay) 37295718399fSFrançois Tigeot continue; 37305718399fSFrançois Tigeot } 37315718399fSFrançois Tigeot if (drm_mode_vrefresh(ptr) > 61) 37325718399fSFrançois Tigeot continue; 37335718399fSFrançois Tigeot mode = drm_mode_duplicate(dev, ptr); 37345718399fSFrançois Tigeot if (mode) { 37355718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 37365718399fSFrançois Tigeot num_modes++; 37375718399fSFrançois Tigeot } 37385718399fSFrançois Tigeot } 37395718399fSFrançois Tigeot return num_modes; 37405718399fSFrançois Tigeot } 37416e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_modes_noedid); 374287cc1051SMichael Neumann 3743ba55f2f5SFrançois Tigeot /** 3744ba55f2f5SFrançois Tigeot * drm_set_preferred_mode - Sets the preferred mode of a connector 3745ba55f2f5SFrançois Tigeot * @connector: connector whose mode list should be processed 3746ba55f2f5SFrançois Tigeot * @hpref: horizontal resolution of preferred mode 3747ba55f2f5SFrançois Tigeot * @vpref: vertical resolution of preferred mode 3748ba55f2f5SFrançois Tigeot * 3749ba55f2f5SFrançois Tigeot * Marks a mode as preferred if it matches the resolution specified by @hpref 3750ba55f2f5SFrançois Tigeot * and @vpref. 3751ba55f2f5SFrançois Tigeot */ 37529edbd4a0SFrançois Tigeot void drm_set_preferred_mode(struct drm_connector *connector, 37539edbd4a0SFrançois Tigeot int hpref, int vpref) 37549edbd4a0SFrançois Tigeot { 37559edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 37569edbd4a0SFrançois Tigeot 37579edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 3758ba55f2f5SFrançois Tigeot if (mode->hdisplay == hpref && 3759ba55f2f5SFrançois Tigeot mode->vdisplay == vpref) 37609edbd4a0SFrançois Tigeot mode->type |= DRM_MODE_TYPE_PREFERRED; 37619edbd4a0SFrançois Tigeot } 37629edbd4a0SFrançois Tigeot } 37639edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_set_preferred_mode); 37649edbd4a0SFrançois Tigeot 376587cc1051SMichael Neumann /** 376687cc1051SMichael Neumann * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with 376787cc1051SMichael Neumann * data from a DRM display mode 376887cc1051SMichael Neumann * @frame: HDMI AVI infoframe 376987cc1051SMichael Neumann * @mode: DRM display mode 377087cc1051SMichael Neumann * 3771ba55f2f5SFrançois Tigeot * Return: 0 on success or a negative error code on failure. 377287cc1051SMichael Neumann */ 377387cc1051SMichael Neumann int 377487cc1051SMichael Neumann drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, 377587cc1051SMichael Neumann const struct drm_display_mode *mode) 377687cc1051SMichael Neumann { 377787cc1051SMichael Neumann int err; 377887cc1051SMichael Neumann 377987cc1051SMichael Neumann if (!frame || !mode) 378087cc1051SMichael Neumann return -EINVAL; 378187cc1051SMichael Neumann 378287cc1051SMichael Neumann err = hdmi_avi_infoframe_init(frame); 378387cc1051SMichael Neumann if (err < 0) 378487cc1051SMichael Neumann return err; 378587cc1051SMichael Neumann 37869edbd4a0SFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLCLK) 37879edbd4a0SFrançois Tigeot frame->pixel_repeat = 1; 37889edbd4a0SFrançois Tigeot 378987cc1051SMichael Neumann frame->video_code = drm_match_cea_mode(mode); 379087cc1051SMichael Neumann 379187cc1051SMichael Neumann frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; 3792ba55f2f5SFrançois Tigeot 3793*24edb884SFrançois Tigeot /* 3794*24edb884SFrançois Tigeot * Populate picture aspect ratio from either 3795*24edb884SFrançois Tigeot * user input (if specified) or from the CEA mode list. 3796*24edb884SFrançois Tigeot */ 3797*24edb884SFrançois Tigeot if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || 3798*24edb884SFrançois Tigeot mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) 3799*24edb884SFrançois Tigeot frame->picture_aspect = mode->picture_aspect_ratio; 3800*24edb884SFrançois Tigeot else if (frame->video_code > 0) 3801ba55f2f5SFrançois Tigeot frame->picture_aspect = drm_get_cea_aspect_ratio( 3802ba55f2f5SFrançois Tigeot frame->video_code); 3803ba55f2f5SFrançois Tigeot 380487cc1051SMichael Neumann frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; 3805ba55f2f5SFrançois Tigeot frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; 380687cc1051SMichael Neumann 380787cc1051SMichael Neumann return 0; 380887cc1051SMichael Neumann } 380987cc1051SMichael Neumann EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); 38109edbd4a0SFrançois Tigeot 38119edbd4a0SFrançois Tigeot static enum hdmi_3d_structure 38129edbd4a0SFrançois Tigeot s3d_structure_from_display_mode(const struct drm_display_mode *mode) 38139edbd4a0SFrançois Tigeot { 38149edbd4a0SFrançois Tigeot u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK; 38159edbd4a0SFrançois Tigeot 38169edbd4a0SFrançois Tigeot switch (layout) { 38179edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FRAME_PACKING: 38189edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FRAME_PACKING; 38199edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: 38209edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE; 38219edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: 38229edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE; 38239edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: 38249edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL; 38259edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH: 38269edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH; 38279edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: 38289edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; 38299edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: 38309edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM; 38319edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: 38329edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF; 38339edbd4a0SFrançois Tigeot default: 38349edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_INVALID; 38359edbd4a0SFrançois Tigeot } 38369edbd4a0SFrançois Tigeot } 38379edbd4a0SFrançois Tigeot 38389edbd4a0SFrançois Tigeot /** 38399edbd4a0SFrançois Tigeot * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with 38409edbd4a0SFrançois Tigeot * data from a DRM display mode 38419edbd4a0SFrançois Tigeot * @frame: HDMI vendor infoframe 38429edbd4a0SFrançois Tigeot * @mode: DRM display mode 38439edbd4a0SFrançois Tigeot * 38449edbd4a0SFrançois Tigeot * Note that there's is a need to send HDMI vendor infoframes only when using a 38459edbd4a0SFrançois Tigeot * 4k or stereoscopic 3D mode. So when giving any other mode as input this 38469edbd4a0SFrançois Tigeot * function will return -EINVAL, error that can be safely ignored. 38479edbd4a0SFrançois Tigeot * 3848ba55f2f5SFrançois Tigeot * Return: 0 on success or a negative error code on failure. 38499edbd4a0SFrançois Tigeot */ 38509edbd4a0SFrançois Tigeot int 38519edbd4a0SFrançois Tigeot drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, 38529edbd4a0SFrançois Tigeot const struct drm_display_mode *mode) 38539edbd4a0SFrançois Tigeot { 38549edbd4a0SFrançois Tigeot int err; 38559edbd4a0SFrançois Tigeot u32 s3d_flags; 38569edbd4a0SFrançois Tigeot u8 vic; 38579edbd4a0SFrançois Tigeot 38589edbd4a0SFrançois Tigeot if (!frame || !mode) 38599edbd4a0SFrançois Tigeot return -EINVAL; 38609edbd4a0SFrançois Tigeot 38619edbd4a0SFrançois Tigeot vic = drm_match_hdmi_mode(mode); 38629edbd4a0SFrançois Tigeot s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; 38639edbd4a0SFrançois Tigeot 38649edbd4a0SFrançois Tigeot if (!vic && !s3d_flags) 38659edbd4a0SFrançois Tigeot return -EINVAL; 38669edbd4a0SFrançois Tigeot 38679edbd4a0SFrançois Tigeot if (vic && s3d_flags) 38689edbd4a0SFrançois Tigeot return -EINVAL; 38699edbd4a0SFrançois Tigeot 38709edbd4a0SFrançois Tigeot err = hdmi_vendor_infoframe_init(frame); 38719edbd4a0SFrançois Tigeot if (err < 0) 38729edbd4a0SFrançois Tigeot return err; 38739edbd4a0SFrançois Tigeot 38749edbd4a0SFrançois Tigeot if (vic) 38759edbd4a0SFrançois Tigeot frame->vic = vic; 38769edbd4a0SFrançois Tigeot else 38779edbd4a0SFrançois Tigeot frame->s3d_struct = s3d_structure_from_display_mode(mode); 38789edbd4a0SFrançois Tigeot 38799edbd4a0SFrançois Tigeot return 0; 38809edbd4a0SFrançois Tigeot } 38819edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); 3882