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> 36*9edbd4a0SFranç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) 75*9edbd4a0SFrançois Tigeot /* Force 8bpc */ 76*9edbd4a0SFrançois Tigeot #define EDID_QUIRK_FORCE_8BPC (1 << 8) 775718399fSFrançois Tigeot 785718399fSFrançois Tigeot struct detailed_mode_closure { 795718399fSFrançois Tigeot struct drm_connector *connector; 805718399fSFrançois Tigeot struct edid *edid; 815718399fSFrançois Tigeot bool preferred; 825718399fSFrançois Tigeot u32 quirks; 835718399fSFrançois Tigeot int modes; 845718399fSFrançois Tigeot }; 855718399fSFrançois Tigeot 865718399fSFrançois Tigeot #define LEVEL_DMT 0 875718399fSFrançois Tigeot #define LEVEL_GTF 1 885718399fSFrançois Tigeot #define LEVEL_GTF2 2 895718399fSFrançois Tigeot #define LEVEL_CVT 3 905718399fSFrançois Tigeot 915718399fSFrançois Tigeot static struct edid_quirk { 926e29dde8SFrançois Tigeot char vendor[4]; 935718399fSFrançois Tigeot int product_id; 945718399fSFrançois Tigeot u32 quirks; 955718399fSFrançois Tigeot } edid_quirk_list[] = { 965718399fSFrançois Tigeot /* Acer AL1706 */ 975718399fSFrançois Tigeot { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, 985718399fSFrançois Tigeot /* Acer F51 */ 995718399fSFrançois Tigeot { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, 1005718399fSFrançois Tigeot /* Unknown Acer */ 1015718399fSFrançois Tigeot { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1025718399fSFrançois Tigeot 1035718399fSFrançois Tigeot /* Belinea 10 15 55 */ 1045718399fSFrançois Tigeot { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, 1055718399fSFrançois Tigeot { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, 1065718399fSFrançois Tigeot 1075718399fSFrançois Tigeot /* Envision Peripherals, Inc. EN-7100e */ 1085718399fSFrançois Tigeot { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, 1095718399fSFrançois Tigeot /* Envision EN2028 */ 1105718399fSFrançois Tigeot { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 }, 1115718399fSFrançois Tigeot 1125718399fSFrançois Tigeot /* Funai Electronics PM36B */ 1135718399fSFrançois Tigeot { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | 1145718399fSFrançois Tigeot EDID_QUIRK_DETAILED_IN_CM }, 1155718399fSFrançois Tigeot 1165718399fSFrançois Tigeot /* LG Philips LCD LP154W01-A5 */ 1175718399fSFrançois Tigeot { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1185718399fSFrançois Tigeot { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, 1195718399fSFrançois Tigeot 1205718399fSFrançois Tigeot /* Philips 107p5 CRT */ 1215718399fSFrançois Tigeot { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1225718399fSFrançois Tigeot 1235718399fSFrançois Tigeot /* Proview AY765C */ 1245718399fSFrançois Tigeot { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, 1255718399fSFrançois Tigeot 1265718399fSFrançois Tigeot /* Samsung SyncMaster 205BW. Note: irony */ 1275718399fSFrançois Tigeot { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, 1285718399fSFrançois Tigeot /* Samsung SyncMaster 22[5-6]BW */ 1295718399fSFrançois Tigeot { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, 1305718399fSFrançois Tigeot { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, 1316e29dde8SFrançois Tigeot 1326e29dde8SFrançois Tigeot /* ViewSonic VA2026w */ 1336e29dde8SFrançois Tigeot { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, 134*9edbd4a0SFrançois Tigeot 135*9edbd4a0SFrançois Tigeot /* Medion MD 30217 PG */ 136*9edbd4a0SFrançois Tigeot { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, 137*9edbd4a0SFrançois Tigeot 138*9edbd4a0SFrançois Tigeot /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ 139*9edbd4a0SFrançois Tigeot { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, 1405718399fSFrançois Tigeot }; 1415718399fSFrançois Tigeot 142d82bf20eSFrançois Tigeot /* 143d82bf20eSFrançois Tigeot * Autogenerated from the DMT spec. 144d82bf20eSFrançois Tigeot * This table is copied from xfree86/modes/xf86EdidModes.c. 145d82bf20eSFrançois Tigeot */ 146d82bf20eSFrançois Tigeot static const struct drm_display_mode drm_dmt_modes[] = { 147d82bf20eSFrançois Tigeot /* 640x350@85Hz */ 148d82bf20eSFrançois Tigeot { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 149d82bf20eSFrançois Tigeot 736, 832, 0, 350, 382, 385, 445, 0, 150d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 151d82bf20eSFrançois Tigeot /* 640x400@85Hz */ 152d82bf20eSFrançois Tigeot { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 153d82bf20eSFrançois Tigeot 736, 832, 0, 400, 401, 404, 445, 0, 154d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 155d82bf20eSFrançois Tigeot /* 720x400@85Hz */ 156d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, 157d82bf20eSFrançois Tigeot 828, 936, 0, 400, 401, 404, 446, 0, 158d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 159d82bf20eSFrançois Tigeot /* 640x480@60Hz */ 160d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 161d82bf20eSFrançois Tigeot 752, 800, 0, 480, 489, 492, 525, 0, 162d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 163d82bf20eSFrançois Tigeot /* 640x480@72Hz */ 164d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 165d82bf20eSFrançois Tigeot 704, 832, 0, 480, 489, 492, 520, 0, 166d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 167d82bf20eSFrançois Tigeot /* 640x480@75Hz */ 168d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 169d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 170d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 171d82bf20eSFrançois Tigeot /* 640x480@85Hz */ 172d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, 173d82bf20eSFrançois Tigeot 752, 832, 0, 480, 481, 484, 509, 0, 174d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 175d82bf20eSFrançois Tigeot /* 800x600@56Hz */ 176d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 177d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 178d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 179d82bf20eSFrançois Tigeot /* 800x600@60Hz */ 180d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 181d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 182d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 183d82bf20eSFrançois Tigeot /* 800x600@72Hz */ 184d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 185d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 186d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 187d82bf20eSFrançois Tigeot /* 800x600@75Hz */ 188d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 189d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 190d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 191d82bf20eSFrançois Tigeot /* 800x600@85Hz */ 192d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, 193d82bf20eSFrançois Tigeot 896, 1048, 0, 600, 601, 604, 631, 0, 194d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 195d82bf20eSFrançois Tigeot /* 800x600@120Hz RB */ 196d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848, 197d82bf20eSFrançois Tigeot 880, 960, 0, 600, 603, 607, 636, 0, 198d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 199d82bf20eSFrançois Tigeot /* 848x480@60Hz */ 200d82bf20eSFrançois Tigeot { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, 201d82bf20eSFrançois Tigeot 976, 1088, 0, 480, 486, 494, 517, 0, 202d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 203d82bf20eSFrançois Tigeot /* 1024x768@43Hz, interlace */ 204d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, 205d82bf20eSFrançois Tigeot 1208, 1264, 0, 768, 768, 772, 817, 0, 206d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 207d82bf20eSFrançois Tigeot DRM_MODE_FLAG_INTERLACE) }, 208d82bf20eSFrançois Tigeot /* 1024x768@60Hz */ 209d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 210d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 211d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 212d82bf20eSFrançois Tigeot /* 1024x768@70Hz */ 213d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 214d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 215d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, 216d82bf20eSFrançois Tigeot /* 1024x768@75Hz */ 217d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, 218d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 219d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 220d82bf20eSFrançois Tigeot /* 1024x768@85Hz */ 221d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, 222d82bf20eSFrançois Tigeot 1168, 1376, 0, 768, 769, 772, 808, 0, 223d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 224d82bf20eSFrançois Tigeot /* 1024x768@120Hz RB */ 225d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072, 226d82bf20eSFrançois Tigeot 1104, 1184, 0, 768, 771, 775, 813, 0, 227d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 228d82bf20eSFrançois Tigeot /* 1152x864@75Hz */ 229d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 230d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 231d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 232d82bf20eSFrançois Tigeot /* 1280x768@60Hz RB */ 233d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328, 234d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 790, 0, 235d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 236d82bf20eSFrançois Tigeot /* 1280x768@60Hz */ 237d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, 238d82bf20eSFrançois Tigeot 1472, 1664, 0, 768, 771, 778, 798, 0, 239d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 240d82bf20eSFrançois Tigeot /* 1280x768@75Hz */ 241d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, 242d82bf20eSFrançois Tigeot 1488, 1696, 0, 768, 771, 778, 805, 0, 243d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 244d82bf20eSFrançois Tigeot /* 1280x768@85Hz */ 245d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, 246d82bf20eSFrançois Tigeot 1496, 1712, 0, 768, 771, 778, 809, 0, 247d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 248d82bf20eSFrançois Tigeot /* 1280x768@120Hz RB */ 249d82bf20eSFrançois Tigeot { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328, 250d82bf20eSFrançois Tigeot 1360, 1440, 0, 768, 771, 778, 813, 0, 251d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 252d82bf20eSFrançois Tigeot /* 1280x800@60Hz RB */ 253d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328, 254d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 823, 0, 255d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 256d82bf20eSFrançois Tigeot /* 1280x800@60Hz */ 257d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, 258d82bf20eSFrançois Tigeot 1480, 1680, 0, 800, 803, 809, 831, 0, 259d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 260d82bf20eSFrançois Tigeot /* 1280x800@75Hz */ 261d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, 262d82bf20eSFrançois Tigeot 1488, 1696, 0, 800, 803, 809, 838, 0, 263d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 264d82bf20eSFrançois Tigeot /* 1280x800@85Hz */ 265d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, 266d82bf20eSFrançois Tigeot 1496, 1712, 0, 800, 803, 809, 843, 0, 267d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 268d82bf20eSFrançois Tigeot /* 1280x800@120Hz RB */ 269d82bf20eSFrançois Tigeot { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328, 270d82bf20eSFrançois Tigeot 1360, 1440, 0, 800, 803, 809, 847, 0, 271d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 272d82bf20eSFrançois Tigeot /* 1280x960@60Hz */ 273d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, 274d82bf20eSFrançois Tigeot 1488, 1800, 0, 960, 961, 964, 1000, 0, 275d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 276d82bf20eSFrançois Tigeot /* 1280x960@85Hz */ 277d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, 278d82bf20eSFrançois Tigeot 1504, 1728, 0, 960, 961, 964, 1011, 0, 279d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 280d82bf20eSFrançois Tigeot /* 1280x960@120Hz RB */ 281d82bf20eSFrançois Tigeot { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328, 282d82bf20eSFrançois Tigeot 1360, 1440, 0, 960, 963, 967, 1017, 0, 283d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 284d82bf20eSFrançois Tigeot /* 1280x1024@60Hz */ 285d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, 286d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 287d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 288d82bf20eSFrançois Tigeot /* 1280x1024@75Hz */ 289d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 290d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 291d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 292d82bf20eSFrançois Tigeot /* 1280x1024@85Hz */ 293d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, 294d82bf20eSFrançois Tigeot 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, 295d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 296d82bf20eSFrançois Tigeot /* 1280x1024@120Hz RB */ 297d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328, 298d82bf20eSFrançois Tigeot 1360, 1440, 0, 1024, 1027, 1034, 1084, 0, 299d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 300d82bf20eSFrançois Tigeot /* 1360x768@60Hz */ 301d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, 302d82bf20eSFrançois Tigeot 1536, 1792, 0, 768, 771, 777, 795, 0, 303d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 304d82bf20eSFrançois Tigeot /* 1360x768@120Hz RB */ 305d82bf20eSFrançois Tigeot { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408, 306d82bf20eSFrançois Tigeot 1440, 1520, 0, 768, 771, 776, 813, 0, 307d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 308d82bf20eSFrançois Tigeot /* 1400x1050@60Hz RB */ 309d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448, 310d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1080, 0, 311d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 312d82bf20eSFrançois Tigeot /* 1400x1050@60Hz */ 313d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, 314d82bf20eSFrançois Tigeot 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 315d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 316d82bf20eSFrançois Tigeot /* 1400x1050@75Hz */ 317d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, 318d82bf20eSFrançois Tigeot 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, 319d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 320d82bf20eSFrançois Tigeot /* 1400x1050@85Hz */ 321d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, 322d82bf20eSFrançois Tigeot 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, 323d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 324d82bf20eSFrançois Tigeot /* 1400x1050@120Hz RB */ 325d82bf20eSFrançois Tigeot { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448, 326d82bf20eSFrançois Tigeot 1480, 1560, 0, 1050, 1053, 1057, 1112, 0, 327d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 328d82bf20eSFrançois Tigeot /* 1440x900@60Hz RB */ 329d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488, 330d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 926, 0, 331d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 332d82bf20eSFrançois Tigeot /* 1440x900@60Hz */ 333d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, 334d82bf20eSFrançois Tigeot 1672, 1904, 0, 900, 903, 909, 934, 0, 335d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 336d82bf20eSFrançois Tigeot /* 1440x900@75Hz */ 337d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, 338d82bf20eSFrançois Tigeot 1688, 1936, 0, 900, 903, 909, 942, 0, 339d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 340d82bf20eSFrançois Tigeot /* 1440x900@85Hz */ 341d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, 342d82bf20eSFrançois Tigeot 1696, 1952, 0, 900, 903, 909, 948, 0, 343d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 344d82bf20eSFrançois Tigeot /* 1440x900@120Hz RB */ 345d82bf20eSFrançois Tigeot { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488, 346d82bf20eSFrançois Tigeot 1520, 1600, 0, 900, 903, 909, 953, 0, 347d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 348d82bf20eSFrançois Tigeot /* 1600x1200@60Hz */ 349d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, 350d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 351d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 352d82bf20eSFrançois Tigeot /* 1600x1200@65Hz */ 353d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, 354d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 355d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 356d82bf20eSFrançois Tigeot /* 1600x1200@70Hz */ 357d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, 358d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 359d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 360d82bf20eSFrançois Tigeot /* 1600x1200@75Hz */ 361d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664, 362d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 363d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 364d82bf20eSFrançois Tigeot /* 1600x1200@85Hz */ 365d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, 366d82bf20eSFrançois Tigeot 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, 367d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 368d82bf20eSFrançois Tigeot /* 1600x1200@120Hz RB */ 369d82bf20eSFrançois Tigeot { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648, 370d82bf20eSFrançois Tigeot 1680, 1760, 0, 1200, 1203, 1207, 1271, 0, 371d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 372d82bf20eSFrançois Tigeot /* 1680x1050@60Hz RB */ 373d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728, 374d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1080, 0, 375d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 376d82bf20eSFrançois Tigeot /* 1680x1050@60Hz */ 377d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, 378d82bf20eSFrançois Tigeot 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 379d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 380d82bf20eSFrançois Tigeot /* 1680x1050@75Hz */ 381d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, 382d82bf20eSFrançois Tigeot 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, 383d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 384d82bf20eSFrançois Tigeot /* 1680x1050@85Hz */ 385d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, 386d82bf20eSFrançois Tigeot 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, 387d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 388d82bf20eSFrançois Tigeot /* 1680x1050@120Hz RB */ 389d82bf20eSFrançois Tigeot { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728, 390d82bf20eSFrançois Tigeot 1760, 1840, 0, 1050, 1053, 1059, 1112, 0, 391d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 392d82bf20eSFrançois Tigeot /* 1792x1344@60Hz */ 393d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, 394d82bf20eSFrançois Tigeot 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, 395d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 396d82bf20eSFrançois Tigeot /* 1792x1344@75Hz */ 397d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, 398d82bf20eSFrançois Tigeot 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, 399d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 400d82bf20eSFrançois Tigeot /* 1792x1344@120Hz RB */ 401d82bf20eSFrançois Tigeot { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840, 402d82bf20eSFrançois Tigeot 1872, 1952, 0, 1344, 1347, 1351, 1423, 0, 403d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 404d82bf20eSFrançois Tigeot /* 1856x1392@60Hz */ 405d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, 406d82bf20eSFrançois Tigeot 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, 407d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 408d82bf20eSFrançois Tigeot /* 1856x1392@75Hz */ 409d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, 410d82bf20eSFrançois Tigeot 2208, 2560, 0, 1392, 1395, 1399, 1500, 0, 411d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 412d82bf20eSFrançois Tigeot /* 1856x1392@120Hz RB */ 413d82bf20eSFrançois Tigeot { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904, 414d82bf20eSFrançois Tigeot 1936, 2016, 0, 1392, 1395, 1399, 1474, 0, 415d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 416d82bf20eSFrançois Tigeot /* 1920x1200@60Hz RB */ 417d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968, 418d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1235, 0, 419d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 420d82bf20eSFrançois Tigeot /* 1920x1200@60Hz */ 421d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, 422d82bf20eSFrançois Tigeot 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 423d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 424d82bf20eSFrançois Tigeot /* 1920x1200@75Hz */ 425d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, 426d82bf20eSFrançois Tigeot 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, 427d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 428d82bf20eSFrançois Tigeot /* 1920x1200@85Hz */ 429d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, 430d82bf20eSFrançois Tigeot 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, 431d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 432d82bf20eSFrançois Tigeot /* 1920x1200@120Hz RB */ 433d82bf20eSFrançois Tigeot { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968, 434d82bf20eSFrançois Tigeot 2000, 2080, 0, 1200, 1203, 1209, 1271, 0, 435d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 436d82bf20eSFrançois Tigeot /* 1920x1440@60Hz */ 437d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, 438d82bf20eSFrançois Tigeot 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, 439d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 440d82bf20eSFrançois Tigeot /* 1920x1440@75Hz */ 441d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, 442d82bf20eSFrançois Tigeot 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, 443d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 444d82bf20eSFrançois Tigeot /* 1920x1440@120Hz RB */ 445d82bf20eSFrançois Tigeot { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968, 446d82bf20eSFrançois Tigeot 2000, 2080, 0, 1440, 1443, 1447, 1525, 0, 447d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 448d82bf20eSFrançois Tigeot /* 2560x1600@60Hz RB */ 449d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608, 450d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1646, 0, 451d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 452d82bf20eSFrançois Tigeot /* 2560x1600@60Hz */ 453d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, 454d82bf20eSFrançois Tigeot 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 455d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 456d82bf20eSFrançois Tigeot /* 2560x1600@75HZ */ 457d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, 458d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, 459d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 460d82bf20eSFrançois Tigeot /* 2560x1600@85HZ */ 461d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, 462d82bf20eSFrançois Tigeot 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, 463d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, 464d82bf20eSFrançois Tigeot /* 2560x1600@120Hz RB */ 465d82bf20eSFrançois Tigeot { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608, 466d82bf20eSFrançois Tigeot 2640, 2720, 0, 1600, 1603, 1609, 1694, 0, 467d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, 468d82bf20eSFrançois Tigeot }; 469d82bf20eSFrançois Tigeot 470*9edbd4a0SFrançois Tigeot /* 471*9edbd4a0SFrançois Tigeot * These more or less come from the DMT spec. The 720x400 modes are 472*9edbd4a0SFrançois Tigeot * inferred from historical 80x25 practice. The 640x480@67 and 832x624@75 473*9edbd4a0SFrançois Tigeot * modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode 474*9edbd4a0SFrançois Tigeot * should be 1152x870, again for the Mac, but instead we use the x864 DMT 475*9edbd4a0SFrançois Tigeot * mode. 476*9edbd4a0SFrançois Tigeot * 477*9edbd4a0SFrançois Tigeot * The DMT modes have been fact-checked; the rest are mild guesses. 478*9edbd4a0SFrançois Tigeot */ 479d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_est_modes[] = { 480d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 481d82bf20eSFrançois Tigeot 968, 1056, 0, 600, 601, 605, 628, 0, 482d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ 483d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, 484d82bf20eSFrançois Tigeot 896, 1024, 0, 600, 601, 603, 625, 0, 485d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ 486d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, 487d82bf20eSFrançois Tigeot 720, 840, 0, 480, 481, 484, 500, 0, 488d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ 489d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, 490d82bf20eSFrançois Tigeot 704, 832, 0, 480, 489, 491, 520, 0, 491d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ 492d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, 493d82bf20eSFrançois Tigeot 768, 864, 0, 480, 483, 486, 525, 0, 494d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ 495d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, 496d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 497d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ 498d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, 499d82bf20eSFrançois Tigeot 846, 900, 0, 400, 421, 423, 449, 0, 500d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ 501d82bf20eSFrançois Tigeot { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, 502d82bf20eSFrançois Tigeot 846, 900, 0, 400, 412, 414, 449, 0, 503d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ 504d82bf20eSFrançois Tigeot { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, 505d82bf20eSFrançois Tigeot 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, 506d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ 507d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, 508d82bf20eSFrançois Tigeot 1136, 1312, 0, 768, 769, 772, 800, 0, 509d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ 510d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, 511d82bf20eSFrançois Tigeot 1184, 1328, 0, 768, 771, 777, 806, 0, 512d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ 513d82bf20eSFrançois Tigeot { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 514d82bf20eSFrançois Tigeot 1184, 1344, 0, 768, 771, 777, 806, 0, 515d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ 516d82bf20eSFrançois Tigeot { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, 517d82bf20eSFrançois Tigeot 1208, 1264, 0, 768, 768, 776, 817, 0, 518d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ 519d82bf20eSFrançois Tigeot { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, 520d82bf20eSFrançois Tigeot 928, 1152, 0, 624, 625, 628, 667, 0, 521d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ 522d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, 523d82bf20eSFrançois Tigeot 896, 1056, 0, 600, 601, 604, 625, 0, 524d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ 525d82bf20eSFrançois Tigeot { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, 526d82bf20eSFrançois Tigeot 976, 1040, 0, 600, 637, 643, 666, 0, 527d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ 528d82bf20eSFrançois Tigeot { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, 529d82bf20eSFrançois Tigeot 1344, 1600, 0, 864, 865, 868, 900, 0, 530d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ 531d82bf20eSFrançois Tigeot }; 532d82bf20eSFrançois Tigeot 533d82bf20eSFrançois Tigeot struct minimode { 534d82bf20eSFrançois Tigeot short w; 535d82bf20eSFrançois Tigeot short h; 536d82bf20eSFrançois Tigeot short r; 537d82bf20eSFrançois Tigeot short rb; 538d82bf20eSFrançois Tigeot }; 539d82bf20eSFrançois Tigeot 540d82bf20eSFrançois Tigeot static const struct minimode est3_modes[] = { 541d82bf20eSFrançois Tigeot /* byte 6 */ 542d82bf20eSFrançois Tigeot { 640, 350, 85, 0 }, 543d82bf20eSFrançois Tigeot { 640, 400, 85, 0 }, 544d82bf20eSFrançois Tigeot { 720, 400, 85, 0 }, 545d82bf20eSFrançois Tigeot { 640, 480, 85, 0 }, 546d82bf20eSFrançois Tigeot { 848, 480, 60, 0 }, 547d82bf20eSFrançois Tigeot { 800, 600, 85, 0 }, 548d82bf20eSFrançois Tigeot { 1024, 768, 85, 0 }, 549d82bf20eSFrançois Tigeot { 1152, 864, 75, 0 }, 550d82bf20eSFrançois Tigeot /* byte 7 */ 551d82bf20eSFrançois Tigeot { 1280, 768, 60, 1 }, 552d82bf20eSFrançois Tigeot { 1280, 768, 60, 0 }, 553d82bf20eSFrançois Tigeot { 1280, 768, 75, 0 }, 554d82bf20eSFrançois Tigeot { 1280, 768, 85, 0 }, 555d82bf20eSFrançois Tigeot { 1280, 960, 60, 0 }, 556d82bf20eSFrançois Tigeot { 1280, 960, 85, 0 }, 557d82bf20eSFrançois Tigeot { 1280, 1024, 60, 0 }, 558d82bf20eSFrançois Tigeot { 1280, 1024, 85, 0 }, 559d82bf20eSFrançois Tigeot /* byte 8 */ 560d82bf20eSFrançois Tigeot { 1360, 768, 60, 0 }, 561d82bf20eSFrançois Tigeot { 1440, 900, 60, 1 }, 562d82bf20eSFrançois Tigeot { 1440, 900, 60, 0 }, 563d82bf20eSFrançois Tigeot { 1440, 900, 75, 0 }, 564d82bf20eSFrançois Tigeot { 1440, 900, 85, 0 }, 565d82bf20eSFrançois Tigeot { 1400, 1050, 60, 1 }, 566d82bf20eSFrançois Tigeot { 1400, 1050, 60, 0 }, 567d82bf20eSFrançois Tigeot { 1400, 1050, 75, 0 }, 568d82bf20eSFrançois Tigeot /* byte 9 */ 569d82bf20eSFrançois Tigeot { 1400, 1050, 85, 0 }, 570d82bf20eSFrançois Tigeot { 1680, 1050, 60, 1 }, 571d82bf20eSFrançois Tigeot { 1680, 1050, 60, 0 }, 572d82bf20eSFrançois Tigeot { 1680, 1050, 75, 0 }, 573d82bf20eSFrançois Tigeot { 1680, 1050, 85, 0 }, 574d82bf20eSFrançois Tigeot { 1600, 1200, 60, 0 }, 575d82bf20eSFrançois Tigeot { 1600, 1200, 65, 0 }, 576d82bf20eSFrançois Tigeot { 1600, 1200, 70, 0 }, 577d82bf20eSFrançois Tigeot /* byte 10 */ 578d82bf20eSFrançois Tigeot { 1600, 1200, 75, 0 }, 579d82bf20eSFrançois Tigeot { 1600, 1200, 85, 0 }, 580d82bf20eSFrançois Tigeot { 1792, 1344, 60, 0 }, 581*9edbd4a0SFrançois Tigeot { 1792, 1344, 75, 0 }, 582d82bf20eSFrançois Tigeot { 1856, 1392, 60, 0 }, 583d82bf20eSFrançois Tigeot { 1856, 1392, 75, 0 }, 584d82bf20eSFrançois Tigeot { 1920, 1200, 60, 1 }, 585d82bf20eSFrançois Tigeot { 1920, 1200, 60, 0 }, 586d82bf20eSFrançois Tigeot /* byte 11 */ 587d82bf20eSFrançois Tigeot { 1920, 1200, 75, 0 }, 588d82bf20eSFrançois Tigeot { 1920, 1200, 85, 0 }, 589d82bf20eSFrançois Tigeot { 1920, 1440, 60, 0 }, 590d82bf20eSFrançois Tigeot { 1920, 1440, 75, 0 }, 591d82bf20eSFrançois Tigeot }; 592d82bf20eSFrançois Tigeot 593d82bf20eSFrançois Tigeot static const struct minimode extra_modes[] = { 594d82bf20eSFrançois Tigeot { 1024, 576, 60, 0 }, 595d82bf20eSFrançois Tigeot { 1366, 768, 60, 0 }, 596d82bf20eSFrançois Tigeot { 1600, 900, 60, 0 }, 597d82bf20eSFrançois Tigeot { 1680, 945, 60, 0 }, 598d82bf20eSFrançois Tigeot { 1920, 1080, 60, 0 }, 599d82bf20eSFrançois Tigeot { 2048, 1152, 60, 0 }, 600d82bf20eSFrançois Tigeot { 2048, 1536, 60, 0 }, 601d82bf20eSFrançois Tigeot }; 602d82bf20eSFrançois Tigeot 603d82bf20eSFrançois Tigeot /* 604d82bf20eSFrançois Tigeot * Probably taken from CEA-861 spec. 605d82bf20eSFrançois Tigeot * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. 606d82bf20eSFrançois Tigeot */ 607d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_cea_modes[] = { 608d82bf20eSFrançois Tigeot /* 1 - 640x480@60Hz */ 609d82bf20eSFrançois Tigeot { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 610d82bf20eSFrançois Tigeot 752, 800, 0, 480, 490, 492, 525, 0, 611*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 612*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 613d82bf20eSFrançois Tigeot /* 2 - 720x480@60Hz */ 614d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 615d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 616*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 617*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 618d82bf20eSFrançois Tigeot /* 3 - 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, 621*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 622*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 623d82bf20eSFrançois Tigeot /* 4 - 1280x720@60Hz */ 624d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 625d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 626*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 627*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 628d82bf20eSFrançois Tigeot /* 5 - 1920x1080i@60Hz */ 629d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 630d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 631d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 632*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 633*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 634d82bf20eSFrançois Tigeot /* 6 - 1440x480i@60Hz */ 635d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 636d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 637d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 638*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 639*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 640d82bf20eSFrançois Tigeot /* 7 - 1440x480i@60Hz */ 641d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 642d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 643d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 644*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 645*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 646d82bf20eSFrançois Tigeot /* 8 - 1440x240@60Hz */ 647d82bf20eSFrançois Tigeot { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 648d82bf20eSFrançois Tigeot 1602, 1716, 0, 240, 244, 247, 262, 0, 649d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 650*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 651*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 652d82bf20eSFrançois Tigeot /* 9 - 1440x240@60Hz */ 653d82bf20eSFrançois Tigeot { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 654d82bf20eSFrançois Tigeot 1602, 1716, 0, 240, 244, 247, 262, 0, 655d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 656*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 657*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 658d82bf20eSFrançois Tigeot /* 10 - 2880x480i@60Hz */ 659d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 660d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 661d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 662*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 663*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 664d82bf20eSFrançois Tigeot /* 11 - 2880x480i@60Hz */ 665d82bf20eSFrançois Tigeot { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 666d82bf20eSFrançois Tigeot 3204, 3432, 0, 480, 488, 494, 525, 0, 667d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 668*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 669*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 670d82bf20eSFrançois Tigeot /* 12 - 2880x240@60Hz */ 671d82bf20eSFrançois Tigeot { DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 672d82bf20eSFrançois Tigeot 3204, 3432, 0, 240, 244, 247, 262, 0, 673*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 674*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 675d82bf20eSFrançois Tigeot /* 13 - 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, 678*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 679*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 680d82bf20eSFrançois Tigeot /* 14 - 1440x480@60Hz */ 681d82bf20eSFrançois Tigeot { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472, 682d82bf20eSFrançois Tigeot 1596, 1716, 0, 480, 489, 495, 525, 0, 683*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 684*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 685d82bf20eSFrançois Tigeot /* 15 - 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, 688*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 689*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 690d82bf20eSFrançois Tigeot /* 16 - 1920x1080@60Hz */ 691d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 692d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 693*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 694*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 695d82bf20eSFrançois Tigeot /* 17 - 720x576@50Hz */ 696d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 697d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 698*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 699*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 700d82bf20eSFrançois Tigeot /* 18 - 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, 703*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 704*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 705d82bf20eSFrançois Tigeot /* 19 - 1280x720@50Hz */ 706d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, 707d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 708*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 709*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 710d82bf20eSFrançois Tigeot /* 20 - 1920x1080i@50Hz */ 711d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 712d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 713d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 714*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 715*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 716d82bf20eSFrançois Tigeot /* 21 - 1440x576i@50Hz */ 717d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 718d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 719d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 720*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 721*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 722d82bf20eSFrançois Tigeot /* 22 - 1440x576i@50Hz */ 723d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 724d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 725d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 726*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 727*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 728d82bf20eSFrançois Tigeot /* 23 - 1440x288@50Hz */ 729d82bf20eSFrançois Tigeot { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 730d82bf20eSFrançois Tigeot 1590, 1728, 0, 288, 290, 293, 312, 0, 731d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 732*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 733*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 734d82bf20eSFrançois Tigeot /* 24 - 1440x288@50Hz */ 735d82bf20eSFrançois Tigeot { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 736d82bf20eSFrançois Tigeot 1590, 1728, 0, 288, 290, 293, 312, 0, 737d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 738*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 739*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 740d82bf20eSFrançois Tigeot /* 25 - 2880x576i@50Hz */ 741d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 742d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 743d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 744*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 745*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 746d82bf20eSFrançois Tigeot /* 26 - 2880x576i@50Hz */ 747d82bf20eSFrançois Tigeot { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 748d82bf20eSFrançois Tigeot 3180, 3456, 0, 576, 580, 586, 625, 0, 749d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 750*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 751*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 752d82bf20eSFrançois Tigeot /* 27 - 2880x288@50Hz */ 753d82bf20eSFrançois Tigeot { DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 754d82bf20eSFrançois Tigeot 3180, 3456, 0, 288, 290, 293, 312, 0, 755*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 756*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 757d82bf20eSFrançois Tigeot /* 28 - 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, 760*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 761*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 762d82bf20eSFrançois Tigeot /* 29 - 1440x576@50Hz */ 763d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 764d82bf20eSFrançois Tigeot 1592, 1728, 0, 576, 581, 586, 625, 0, 765*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 766*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 767d82bf20eSFrançois Tigeot /* 30 - 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, 770*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 771*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 772d82bf20eSFrançois Tigeot /* 31 - 1920x1080@50Hz */ 773d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 774d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 775*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 776*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 777d82bf20eSFrançois Tigeot /* 32 - 1920x1080@24Hz */ 778d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, 779d82bf20eSFrançois Tigeot 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, 780*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 781*9edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 782d82bf20eSFrançois Tigeot /* 33 - 1920x1080@25Hz */ 783d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 784d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, 785*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 786*9edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 787d82bf20eSFrançois Tigeot /* 34 - 1920x1080@30Hz */ 788d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 789d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 790*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 791*9edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 792d82bf20eSFrançois Tigeot /* 35 - 2880x480@60Hz */ 793d82bf20eSFrançois Tigeot { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944, 794d82bf20eSFrançois Tigeot 3192, 3432, 0, 480, 489, 495, 525, 0, 795*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 796*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 797d82bf20eSFrançois Tigeot /* 36 - 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, 800*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 801*9edbd4a0SFrançois Tigeot .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 802d82bf20eSFrançois Tigeot /* 37 - 2880x576@50Hz */ 803d82bf20eSFrançois Tigeot { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928, 804d82bf20eSFrançois Tigeot 3184, 3456, 0, 576, 581, 586, 625, 0, 805*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 806*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 807d82bf20eSFrançois Tigeot /* 38 - 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, 810*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 811*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 812d82bf20eSFrançois Tigeot /* 39 - 1920x1080i@50Hz */ 813d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, 814d82bf20eSFrançois Tigeot 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, 815d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | 816*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 817*9edbd4a0SFrançois Tigeot .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 818d82bf20eSFrançois Tigeot /* 40 - 1920x1080i@100Hz */ 819d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 820d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 821d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 822*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 823*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 824d82bf20eSFrançois Tigeot /* 41 - 1280x720@100Hz */ 825d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720, 826d82bf20eSFrançois Tigeot 1760, 1980, 0, 720, 725, 730, 750, 0, 827*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 828*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 829d82bf20eSFrançois Tigeot /* 42 - 720x576@100Hz */ 830d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, 831d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 832*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 833*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 834d82bf20eSFrançois Tigeot /* 43 - 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, 837*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 838*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 839d82bf20eSFrançois Tigeot /* 44 - 1440x576i@100Hz */ 840d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 841d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 842d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 843*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 844*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 845d82bf20eSFrançois Tigeot /* 45 - 1440x576i@100Hz */ 846d82bf20eSFrançois Tigeot { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, 847d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 848d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 849*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_DBLCLK), 850*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 851d82bf20eSFrançois Tigeot /* 46 - 1920x1080i@120Hz */ 852d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 853d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, 854d82bf20eSFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | 855*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE), 856*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 857d82bf20eSFrançois Tigeot /* 47 - 1280x720@120Hz */ 858d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390, 859d82bf20eSFrançois Tigeot 1430, 1650, 0, 720, 725, 730, 750, 0, 860*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 861*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 862d82bf20eSFrançois Tigeot /* 48 - 720x480@120Hz */ 863d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736, 864d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 865*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 866*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 867d82bf20eSFrançois Tigeot /* 49 - 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, 870*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 871*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 872d82bf20eSFrançois Tigeot /* 50 - 1440x480i@120Hz */ 873d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 874d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 875d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 876*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 877*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 878d82bf20eSFrançois Tigeot /* 51 - 1440x480i@120Hz */ 879d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 880d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 881d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 882*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 883*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 884d82bf20eSFrançois Tigeot /* 52 - 720x576@200Hz */ 885d82bf20eSFrançois Tigeot { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732, 886d82bf20eSFrançois Tigeot 796, 864, 0, 576, 581, 586, 625, 0, 887*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 888*9edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 889d82bf20eSFrançois Tigeot /* 53 - 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, 892*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 893*9edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 894d82bf20eSFrançois Tigeot /* 54 - 1440x576i@200Hz */ 895d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 896d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 897d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 898*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 899*9edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 900d82bf20eSFrançois Tigeot /* 55 - 1440x576i@200Hz */ 901d82bf20eSFrançois Tigeot { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 902d82bf20eSFrançois Tigeot 1590, 1728, 0, 576, 580, 586, 625, 0, 903d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 904*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 905*9edbd4a0SFrançois Tigeot .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 906d82bf20eSFrançois Tigeot /* 56 - 720x480@240Hz */ 907d82bf20eSFrançois Tigeot { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736, 908d82bf20eSFrançois Tigeot 798, 858, 0, 480, 489, 495, 525, 0, 909*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 910*9edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 911d82bf20eSFrançois Tigeot /* 57 - 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, 914*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), 915*9edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 916d82bf20eSFrançois Tigeot /* 58 - 1440x480i@240 */ 917d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 918d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 919d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 920*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 921*9edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, 922d82bf20eSFrançois Tigeot /* 59 - 1440x480i@240 */ 923d82bf20eSFrançois Tigeot { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 924d82bf20eSFrançois Tigeot 1602, 1716, 0, 480, 488, 494, 525, 0, 925d82bf20eSFrançois Tigeot DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | 926*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), 927*9edbd4a0SFrançois Tigeot .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 928d82bf20eSFrançois Tigeot /* 60 - 1280x720@24Hz */ 929d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, 930d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 931*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 932*9edbd4a0SFrançois Tigeot .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 933d82bf20eSFrançois Tigeot /* 61 - 1280x720@25Hz */ 934d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700, 935d82bf20eSFrançois Tigeot 3740, 3960, 0, 720, 725, 730, 750, 0, 936*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 937*9edbd4a0SFrançois Tigeot .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 938d82bf20eSFrançois Tigeot /* 62 - 1280x720@30Hz */ 939d82bf20eSFrançois Tigeot { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040, 940d82bf20eSFrançois Tigeot 3080, 3300, 0, 720, 725, 730, 750, 0, 941*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 942*9edbd4a0SFrançois Tigeot .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 943d82bf20eSFrançois Tigeot /* 63 - 1920x1080@120Hz */ 944d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, 945d82bf20eSFrançois Tigeot 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, 946*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 947*9edbd4a0SFrançois Tigeot .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 948d82bf20eSFrançois Tigeot /* 64 - 1920x1080@100Hz */ 949d82bf20eSFrançois Tigeot { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, 950d82bf20eSFrançois Tigeot 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, 951*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 952*9edbd4a0SFrançois Tigeot .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, 953*9edbd4a0SFrançois Tigeot }; 954*9edbd4a0SFrançois Tigeot 955*9edbd4a0SFrançois Tigeot /* 956*9edbd4a0SFrançois Tigeot * HDMI 1.4 4k modes. 957*9edbd4a0SFrançois Tigeot */ 958*9edbd4a0SFrançois Tigeot static const struct drm_display_mode edid_4k_modes[] = { 959*9edbd4a0SFrançois Tigeot /* 1 - 3840x2160@30Hz */ 960*9edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 961*9edbd4a0SFrançois Tigeot 3840, 4016, 4104, 4400, 0, 962*9edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 963*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 964*9edbd4a0SFrançois Tigeot .vrefresh = 30, }, 965*9edbd4a0SFrançois Tigeot /* 2 - 3840x2160@25Hz */ 966*9edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 967*9edbd4a0SFrançois Tigeot 3840, 4896, 4984, 5280, 0, 968*9edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 969*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 970*9edbd4a0SFrançois Tigeot .vrefresh = 25, }, 971*9edbd4a0SFrançois Tigeot /* 3 - 3840x2160@24Hz */ 972*9edbd4a0SFrançois Tigeot { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 973*9edbd4a0SFrançois Tigeot 3840, 5116, 5204, 5500, 0, 974*9edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 975*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 976*9edbd4a0SFrançois Tigeot .vrefresh = 24, }, 977*9edbd4a0SFrançois Tigeot /* 4 - 4096x2160@24Hz (SMPTE) */ 978*9edbd4a0SFrançois Tigeot { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 979*9edbd4a0SFrançois Tigeot 4096, 5116, 5204, 5500, 0, 980*9edbd4a0SFrançois Tigeot 2160, 2168, 2178, 2250, 0, 981*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), 982*9edbd4a0SFrançois Tigeot .vrefresh = 24, }, 983d82bf20eSFrançois Tigeot }; 984d82bf20eSFrançois Tigeot 9855718399fSFrançois Tigeot /*** DDC fetch and block validation ***/ 9865718399fSFrançois Tigeot 9875718399fSFrançois Tigeot static const u8 edid_header[] = { 9885718399fSFrançois Tigeot 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 9895718399fSFrançois Tigeot }; 9905718399fSFrançois Tigeot 9915718399fSFrançois Tigeot /* 9925718399fSFrançois Tigeot * Sanity check the header of the base EDID block. Return 8 if the header 9935718399fSFrançois Tigeot * is perfect, down to 0 if it's totally wrong. 9945718399fSFrançois Tigeot */ 9955718399fSFrançois Tigeot int drm_edid_header_is_valid(const u8 *raw_edid) 9965718399fSFrançois Tigeot { 9975718399fSFrançois Tigeot int i, score = 0; 9985718399fSFrançois Tigeot 9995718399fSFrançois Tigeot for (i = 0; i < sizeof(edid_header); i++) 10005718399fSFrançois Tigeot if (raw_edid[i] == edid_header[i]) 10015718399fSFrançois Tigeot score++; 10025718399fSFrançois Tigeot 10035718399fSFrançois Tigeot return score; 10045718399fSFrançois Tigeot } 1005ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_header_is_valid); 1006ce3d36d7SFrançois Tigeot 1007ce3d36d7SFrançois Tigeot static int edid_fixup __read_mostly = 6; 1008d82bf20eSFrançois Tigeot module_param_named(edid_fixup, edid_fixup, int, 0400); 1009d82bf20eSFrançois Tigeot MODULE_PARM_DESC(edid_fixup, 1010d82bf20eSFrançois Tigeot "Minimum number of valid EDID header bytes (0-8, default 6)"); 10115718399fSFrançois Tigeot 10125718399fSFrançois Tigeot /* 10135718399fSFrançois Tigeot * Sanity check the EDID block (base or extension). Return 0 if the block 10145718399fSFrançois Tigeot * doesn't check out, or 1 if it's valid. 10155718399fSFrançois Tigeot */ 1016ce3d36d7SFrançois Tigeot bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) 10175718399fSFrançois Tigeot { 10185718399fSFrançois Tigeot int i; 10195718399fSFrançois Tigeot u8 csum = 0; 10205718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 10215718399fSFrançois Tigeot 1022*9edbd4a0SFrançois Tigeot if (WARN_ON(!raw_edid)) 1023*9edbd4a0SFrançois Tigeot return false; 1024*9edbd4a0SFrançois Tigeot 1025ce3d36d7SFrançois Tigeot if (edid_fixup > 8 || edid_fixup < 0) 1026ce3d36d7SFrançois Tigeot edid_fixup = 6; 1027ce3d36d7SFrançois Tigeot 1028ce3d36d7SFrançois Tigeot if (block == 0) { 10295718399fSFrançois Tigeot int score = drm_edid_header_is_valid(raw_edid); 10305718399fSFrançois Tigeot if (score == 8) ; 1031ce3d36d7SFrançois Tigeot else if (score >= edid_fixup) { 10325718399fSFrançois Tigeot DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); 10335718399fSFrançois Tigeot memcpy(raw_edid, edid_header, sizeof(edid_header)); 10345718399fSFrançois Tigeot } else { 10355718399fSFrançois Tigeot goto bad; 10365718399fSFrançois Tigeot } 10375718399fSFrançois Tigeot } 10385718399fSFrançois Tigeot 10395718399fSFrançois Tigeot for (i = 0; i < EDID_LENGTH; i++) 10405718399fSFrançois Tigeot csum += raw_edid[i]; 10415718399fSFrançois Tigeot if (csum) { 1042ce3d36d7SFrançois Tigeot if (print_bad_edid) { 10435718399fSFrançois Tigeot DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); 1044ce3d36d7SFrançois Tigeot } 10455718399fSFrançois Tigeot 10465718399fSFrançois Tigeot /* allow CEA to slide through, switches mangle this */ 10475718399fSFrançois Tigeot if (raw_edid[0] != 0x02) 10485718399fSFrançois Tigeot goto bad; 10495718399fSFrançois Tigeot } 10505718399fSFrançois Tigeot 10515718399fSFrançois Tigeot /* per-block-type checks */ 10525718399fSFrançois Tigeot switch (raw_edid[0]) { 10535718399fSFrançois Tigeot case 0: /* base */ 10545718399fSFrançois Tigeot if (edid->version != 1) { 10555718399fSFrançois Tigeot DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); 10565718399fSFrançois Tigeot goto bad; 10575718399fSFrançois Tigeot } 10585718399fSFrançois Tigeot 10595718399fSFrançois Tigeot if (edid->revision > 4) 10605718399fSFrançois Tigeot DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); 10615718399fSFrançois Tigeot break; 10625718399fSFrançois Tigeot 10635718399fSFrançois Tigeot default: 10645718399fSFrançois Tigeot break; 10655718399fSFrançois Tigeot } 10665718399fSFrançois Tigeot 1067*9edbd4a0SFrançois Tigeot return true; 10685718399fSFrançois Tigeot 10695718399fSFrançois Tigeot bad: 1070*9edbd4a0SFrançois Tigeot if (print_bad_edid) { 1071*9edbd4a0SFrançois Tigeot printk(KERN_ERR "Raw EDID:\n"); 10725718399fSFrançois Tigeot for (i = 0; i < EDID_LENGTH; ) { 10735718399fSFrançois Tigeot kprintf("%02x", raw_edid[i]); 10745718399fSFrançois Tigeot i++; 10755718399fSFrançois Tigeot if (i % 16 == 0 || i == EDID_LENGTH) 10765718399fSFrançois Tigeot kprintf("\n"); 10775718399fSFrançois Tigeot else if (i % 8 == 0) 10785718399fSFrançois Tigeot kprintf(" "); 10795718399fSFrançois Tigeot else 10805718399fSFrançois Tigeot kprintf(" "); 10815718399fSFrançois Tigeot } 10825718399fSFrançois Tigeot } 1083*9edbd4a0SFrançois Tigeot return false; 10845718399fSFrançois Tigeot } 1085ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_block_valid); 10865718399fSFrançois Tigeot 10875718399fSFrançois Tigeot /** 10885718399fSFrançois Tigeot * drm_edid_is_valid - sanity check EDID data 10895718399fSFrançois Tigeot * @edid: EDID data 10905718399fSFrançois Tigeot * 10915718399fSFrançois Tigeot * Sanity-check an entire EDID record (including extensions) 10925718399fSFrançois Tigeot */ 10935718399fSFrançois Tigeot bool drm_edid_is_valid(struct edid *edid) 10945718399fSFrançois Tigeot { 10955718399fSFrançois Tigeot int i; 10965718399fSFrançois Tigeot u8 *raw = (u8 *)edid; 10975718399fSFrançois Tigeot 10985718399fSFrançois Tigeot if (!edid) 10995718399fSFrançois Tigeot return false; 11005718399fSFrançois Tigeot 11015718399fSFrançois Tigeot for (i = 0; i <= edid->extensions; i++) 1102ce3d36d7SFrançois Tigeot if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true)) 11035718399fSFrançois Tigeot return false; 11045718399fSFrançois Tigeot 11055718399fSFrançois Tigeot return true; 11065718399fSFrançois Tigeot } 1107ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_is_valid); 11085718399fSFrançois Tigeot 11095718399fSFrançois Tigeot #define DDC_SEGMENT_ADDR 0x30 11105718399fSFrançois Tigeot /** 11115718399fSFrançois Tigeot * Get EDID information via I2C. 11125718399fSFrançois Tigeot * 11135718399fSFrançois Tigeot * \param adapter : i2c device adaptor 11145718399fSFrançois Tigeot * \param buf : EDID data buffer to be filled 11155718399fSFrançois Tigeot * \param len : EDID data buffer length 11165718399fSFrançois Tigeot * \return 0 on success or -1 on failure. 11175718399fSFrançois Tigeot * 11185718399fSFrançois Tigeot * Try to fetch EDID information by calling i2c driver function. 11195718399fSFrançois Tigeot */ 11205718399fSFrançois Tigeot static int 1121d82bf20eSFrançois Tigeot drm_do_probe_ddc_edid(struct device *adapter, unsigned char *buf, 11225718399fSFrançois Tigeot int block, int len) 11235718399fSFrançois Tigeot { 11245718399fSFrançois Tigeot unsigned char start = block * EDID_LENGTH; 11256f486c69SFrançois Tigeot unsigned char segment = block >> 1; 11266f486c69SFrançois Tigeot unsigned char xfers = segment ? 3 : 2; 11275718399fSFrançois Tigeot int ret, retries = 5; 11285718399fSFrançois Tigeot 11295718399fSFrançois Tigeot /* The core i2c driver will automatically retry the transfer if the 11305718399fSFrançois Tigeot * adapter reports EAGAIN. However, we find that bit-banging transfers 11315718399fSFrançois Tigeot * are susceptible to errors under a heavily loaded machine and 11325718399fSFrançois Tigeot * generate spurious NAKs and timeouts. Retrying the transfer 11335718399fSFrançois Tigeot * of the individual block a few times seems to overcome this. 11345718399fSFrançois Tigeot */ 11355718399fSFrançois Tigeot do { 11366e29dde8SFrançois Tigeot struct i2c_msg msgs[] = { 11375718399fSFrançois Tigeot { 11386f486c69SFrançois Tigeot .slave = DDC_SEGMENT_ADDR << 1, 11396f486c69SFrançois Tigeot .flags = 0, 11406f486c69SFrançois Tigeot .len = 1, 11416f486c69SFrançois Tigeot .buf = &segment, 11426f486c69SFrançois Tigeot }, { 11435718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 11446e29dde8SFrançois Tigeot .flags = 0, 11455718399fSFrançois Tigeot .len = 1, 11465718399fSFrançois Tigeot .buf = &start, 11475718399fSFrançois Tigeot }, { 11485718399fSFrançois Tigeot .slave = DDC_ADDR << 1, 11496e29dde8SFrançois Tigeot .flags = I2C_M_RD, 11505718399fSFrançois Tigeot .len = len, 11515718399fSFrançois Tigeot .buf = buf, 11525718399fSFrançois Tigeot } 11535718399fSFrançois Tigeot }; 11546f486c69SFrançois Tigeot 11556f486c69SFrançois Tigeot /* 11566f486c69SFrançois Tigeot * Avoid sending the segment addr to not upset non-compliant ddc 11576f486c69SFrançois Tigeot * monitors. 11586f486c69SFrançois Tigeot */ 11596f486c69SFrançois Tigeot ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers); 11606f486c69SFrançois Tigeot 11615718399fSFrançois Tigeot if (ret != 0) 11625718399fSFrançois Tigeot DRM_DEBUG_KMS("iicbus_transfer countdown %d error %d\n", 11635718399fSFrançois Tigeot retries, ret); 11645718399fSFrançois Tigeot } while (ret != 0 && --retries); 11655718399fSFrançois Tigeot 11665718399fSFrançois Tigeot return (ret == 0 ? 0 : -1); 11675718399fSFrançois Tigeot } 11685718399fSFrançois Tigeot 11695718399fSFrançois Tigeot static bool drm_edid_is_zero(u8 *in_edid, int length) 11705718399fSFrançois Tigeot { 11715718399fSFrançois Tigeot int i; 11725718399fSFrançois Tigeot u32 *raw_edid = (u32 *)in_edid; 11735718399fSFrançois Tigeot 11745718399fSFrançois Tigeot for (i = 0; i < length / 4; i++) 11755718399fSFrançois Tigeot if (*(raw_edid + i) != 0) 11765718399fSFrançois Tigeot return false; 1177d82bf20eSFrançois Tigeot 11785718399fSFrançois Tigeot return true; 11795718399fSFrançois Tigeot } 11805718399fSFrançois Tigeot 11815718399fSFrançois Tigeot static u8 * 1182d82bf20eSFrançois Tigeot drm_do_get_edid(struct drm_connector *connector, struct device *adapter) 11835718399fSFrançois Tigeot { 11845718399fSFrançois Tigeot int i, j = 0, valid_extensions = 0; 11855718399fSFrançois Tigeot u8 *block, *new; 1186ce3d36d7SFrançois Tigeot bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); 11875718399fSFrançois Tigeot 1188*9edbd4a0SFrançois Tigeot if ((block = kmalloc(EDID_LENGTH, M_DRM, M_WAITOK)) == NULL) 1189*9edbd4a0SFrançois Tigeot return NULL; 11905718399fSFrançois Tigeot 11915718399fSFrançois Tigeot /* base block fetch */ 11925718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 11935718399fSFrançois Tigeot if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) 11945718399fSFrançois Tigeot goto out; 1195ce3d36d7SFrançois Tigeot if (drm_edid_block_valid(block, 0, print_bad_edid)) 11965718399fSFrançois Tigeot break; 11975718399fSFrançois Tigeot if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { 11985718399fSFrançois Tigeot connector->null_edid_counter++; 11995718399fSFrançois Tigeot goto carp; 12005718399fSFrançois Tigeot } 12015718399fSFrançois Tigeot } 12025718399fSFrançois Tigeot if (i == 4) 12035718399fSFrançois Tigeot goto carp; 12045718399fSFrançois Tigeot 12055718399fSFrançois Tigeot /* if there's no extensions, we're done */ 12065718399fSFrançois Tigeot if (block[0x7e] == 0) 12075718399fSFrançois Tigeot return block; 12085718399fSFrançois Tigeot 1209d82bf20eSFrançois Tigeot new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 1210ce3d36d7SFrançois Tigeot if (!new) 1211ce3d36d7SFrançois Tigeot goto out; 12125718399fSFrançois Tigeot block = new; 12135718399fSFrançois Tigeot 12145718399fSFrançois Tigeot for (j = 1; j <= block[0x7e]; j++) { 12155718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 12165718399fSFrançois Tigeot if (drm_do_probe_ddc_edid(adapter, 12175718399fSFrançois Tigeot block + (valid_extensions + 1) * EDID_LENGTH, 12185718399fSFrançois Tigeot j, EDID_LENGTH)) 12195718399fSFrançois Tigeot goto out; 1220ce3d36d7SFrançois Tigeot if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) { 12215718399fSFrançois Tigeot valid_extensions++; 12225718399fSFrançois Tigeot break; 12235718399fSFrançois Tigeot } 12245718399fSFrançois Tigeot } 1225d82bf20eSFrançois Tigeot 1226d82bf20eSFrançois Tigeot if (i == 4 && print_bad_edid) { 1227ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, 1228ce3d36d7SFrançois Tigeot "%s: Ignoring invalid EDID block %d.\n", 12295718399fSFrançois Tigeot drm_get_connector_name(connector), j); 1230d82bf20eSFrançois Tigeot 1231d82bf20eSFrançois Tigeot connector->bad_edid_counter++; 1232d82bf20eSFrançois Tigeot } 12335718399fSFrançois Tigeot } 12345718399fSFrançois Tigeot 12355718399fSFrançois Tigeot if (valid_extensions != block[0x7e]) { 12365718399fSFrançois Tigeot block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; 12375718399fSFrançois Tigeot block[0x7e] = valid_extensions; 12385718399fSFrançois Tigeot new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, 12395a3b77d5SFrançois Tigeot M_DRM, M_WAITOK); 1240ce3d36d7SFrançois Tigeot if (!new) 1241ce3d36d7SFrançois Tigeot goto out; 12425718399fSFrançois Tigeot block = new; 12435718399fSFrançois Tigeot } 12445718399fSFrançois Tigeot 12455718399fSFrançois Tigeot return block; 12465718399fSFrançois Tigeot 12475718399fSFrançois Tigeot carp: 1248ce3d36d7SFrançois Tigeot if (print_bad_edid) { 1249ce3d36d7SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", 12505718399fSFrançois Tigeot drm_get_connector_name(connector), j); 1251ce3d36d7SFrançois Tigeot } 1252ce3d36d7SFrançois Tigeot connector->bad_edid_counter++; 12535718399fSFrançois Tigeot 12545718399fSFrançois Tigeot out: 1255158486a6SFrançois Tigeot kfree(block); 12565718399fSFrançois Tigeot return NULL; 12575718399fSFrançois Tigeot } 12585718399fSFrançois Tigeot 12595718399fSFrançois Tigeot /** 12605718399fSFrançois Tigeot * Probe DDC presence. 12615718399fSFrançois Tigeot * 12625718399fSFrançois Tigeot * \param adapter : i2c device adaptor 12635718399fSFrançois Tigeot * \return 1 on success 12645718399fSFrançois Tigeot */ 1265ce3d36d7SFrançois Tigeot bool 1266d82bf20eSFrançois Tigeot drm_probe_ddc(struct device *adapter) 12675718399fSFrançois Tigeot { 12685718399fSFrançois Tigeot unsigned char out; 12695718399fSFrançois Tigeot 12705718399fSFrançois Tigeot return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0); 12715718399fSFrançois Tigeot } 1272ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_probe_ddc); 12735718399fSFrançois Tigeot 12745718399fSFrançois Tigeot /** 12755718399fSFrançois Tigeot * drm_get_edid - get EDID data, if available 12765718399fSFrançois Tigeot * @connector: connector we're probing 12775718399fSFrançois Tigeot * @adapter: i2c adapter to use for DDC 12785718399fSFrançois Tigeot * 12795718399fSFrançois Tigeot * Poke the given i2c channel to grab EDID data if possible. If found, 12805718399fSFrançois Tigeot * attach it to the connector. 12815718399fSFrançois Tigeot * 12825718399fSFrançois Tigeot * Return edid data or NULL if we couldn't find any. 12835718399fSFrançois Tigeot */ 12845718399fSFrançois Tigeot struct edid *drm_get_edid(struct drm_connector *connector, 1285d82bf20eSFrançois Tigeot struct device *adapter) 12865718399fSFrançois Tigeot { 12875718399fSFrançois Tigeot struct edid *edid = NULL; 12885718399fSFrançois Tigeot 12895718399fSFrançois Tigeot if (drm_probe_ddc(adapter)) 12905718399fSFrançois Tigeot edid = (struct edid *)drm_do_get_edid(connector, adapter); 12915718399fSFrançois Tigeot 12925718399fSFrançois Tigeot return edid; 12935718399fSFrançois Tigeot } 1294af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid); 12955718399fSFrançois Tigeot 1296*9edbd4a0SFrançois Tigeot /** 1297*9edbd4a0SFrançois Tigeot * drm_edid_duplicate - duplicate an EDID and the extensions 1298*9edbd4a0SFrançois Tigeot * @edid: EDID to duplicate 1299*9edbd4a0SFrançois Tigeot * 1300*9edbd4a0SFrançois Tigeot * Return duplicate edid or NULL on allocation failure. 1301*9edbd4a0SFrançois Tigeot */ 1302*9edbd4a0SFrançois Tigeot struct edid *drm_edid_duplicate(const struct edid *edid) 1303*9edbd4a0SFrançois Tigeot { 1304*9edbd4a0SFrançois Tigeot return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); 1305*9edbd4a0SFrançois Tigeot } 1306*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_duplicate); 1307*9edbd4a0SFrançois Tigeot 13085718399fSFrançois Tigeot /*** EDID parsing ***/ 13095718399fSFrançois Tigeot 13105718399fSFrançois Tigeot /** 13115718399fSFrançois Tigeot * edid_vendor - match a string against EDID's obfuscated vendor field 13125718399fSFrançois Tigeot * @edid: EDID to match 13135718399fSFrançois Tigeot * @vendor: vendor string 13145718399fSFrançois Tigeot * 13155718399fSFrançois Tigeot * Returns true if @vendor is in @edid, false otherwise 13165718399fSFrançois Tigeot */ 13175718399fSFrançois Tigeot static bool edid_vendor(struct edid *edid, char *vendor) 13185718399fSFrançois Tigeot { 13195718399fSFrançois Tigeot char edid_vendor[3]; 13205718399fSFrançois Tigeot 13215718399fSFrançois Tigeot edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; 13225718399fSFrançois Tigeot edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | 13235718399fSFrançois Tigeot ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; 13245718399fSFrançois Tigeot edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; 13255718399fSFrançois Tigeot 13265718399fSFrançois Tigeot return !strncmp(edid_vendor, vendor, 3); 13275718399fSFrançois Tigeot } 13285718399fSFrançois Tigeot 13295718399fSFrançois Tigeot /** 13305718399fSFrançois Tigeot * edid_get_quirks - return quirk flags for a given EDID 13315718399fSFrançois Tigeot * @edid: EDID to process 13325718399fSFrançois Tigeot * 13335718399fSFrançois Tigeot * This tells subsequent routines what fixes they need to apply. 13345718399fSFrançois Tigeot */ 13355718399fSFrançois Tigeot static u32 edid_get_quirks(struct edid *edid) 13365718399fSFrançois Tigeot { 13375718399fSFrançois Tigeot struct edid_quirk *quirk; 13385718399fSFrançois Tigeot int i; 13395718399fSFrançois Tigeot 13406e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { 13415718399fSFrançois Tigeot quirk = &edid_quirk_list[i]; 13425718399fSFrançois Tigeot 13435718399fSFrançois Tigeot if (edid_vendor(edid, quirk->vendor) && 13445718399fSFrançois Tigeot (EDID_PRODUCT_ID(edid) == quirk->product_id)) 13455718399fSFrançois Tigeot return quirk->quirks; 13465718399fSFrançois Tigeot } 13475718399fSFrançois Tigeot 13485718399fSFrançois Tigeot return 0; 13495718399fSFrançois Tigeot } 13505718399fSFrançois Tigeot 13515718399fSFrançois Tigeot #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) 1352*9edbd4a0SFrançois Tigeot #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t))) 13535718399fSFrançois Tigeot 13545718399fSFrançois Tigeot /** 13555718399fSFrançois Tigeot * edid_fixup_preferred - set preferred modes based on quirk list 13565718399fSFrançois Tigeot * @connector: has mode list to fix up 13575718399fSFrançois Tigeot * @quirks: quirks list 13585718399fSFrançois Tigeot * 13595718399fSFrançois Tigeot * Walk the mode list for @connector, clearing the preferred status 13605718399fSFrançois Tigeot * on existing modes and setting it anew for the right mode ala @quirks. 13615718399fSFrançois Tigeot */ 13625718399fSFrançois Tigeot static void edid_fixup_preferred(struct drm_connector *connector, 13635718399fSFrançois Tigeot u32 quirks) 13645718399fSFrançois Tigeot { 13655718399fSFrançois Tigeot struct drm_display_mode *t, *cur_mode, *preferred_mode; 13665718399fSFrançois Tigeot int target_refresh = 0; 1367*9edbd4a0SFrançois Tigeot int cur_vrefresh, preferred_vrefresh; 13685718399fSFrançois Tigeot 13695718399fSFrançois Tigeot if (list_empty(&connector->probed_modes)) 13705718399fSFrançois Tigeot return; 13715718399fSFrançois Tigeot 13725718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_60) 13735718399fSFrançois Tigeot target_refresh = 60; 13745718399fSFrançois Tigeot if (quirks & EDID_QUIRK_PREFER_LARGE_75) 13755718399fSFrançois Tigeot target_refresh = 75; 13765718399fSFrançois Tigeot 13775718399fSFrançois Tigeot preferred_mode = list_first_entry(&connector->probed_modes, 13785718399fSFrançois Tigeot struct drm_display_mode, head); 13795718399fSFrançois Tigeot 13805718399fSFrançois Tigeot list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { 13815718399fSFrançois Tigeot cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; 13825718399fSFrançois Tigeot 13835718399fSFrançois Tigeot if (cur_mode == preferred_mode) 13845718399fSFrançois Tigeot continue; 13855718399fSFrançois Tigeot 13865718399fSFrançois Tigeot /* Largest mode is preferred */ 13875718399fSFrançois Tigeot if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) 13885718399fSFrançois Tigeot preferred_mode = cur_mode; 13895718399fSFrançois Tigeot 1390*9edbd4a0SFrançois Tigeot cur_vrefresh = cur_mode->vrefresh ? 1391*9edbd4a0SFrançois Tigeot cur_mode->vrefresh : drm_mode_vrefresh(cur_mode); 1392*9edbd4a0SFrançois Tigeot preferred_vrefresh = preferred_mode->vrefresh ? 1393*9edbd4a0SFrançois Tigeot preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode); 13945718399fSFrançois Tigeot /* At a given size, try to get closest to target refresh */ 13955718399fSFrançois Tigeot if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && 1396*9edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) < 1397*9edbd4a0SFrançois Tigeot MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) { 13985718399fSFrançois Tigeot preferred_mode = cur_mode; 13995718399fSFrançois Tigeot } 14005718399fSFrançois Tigeot } 14015718399fSFrançois Tigeot 14025718399fSFrançois Tigeot preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; 14035718399fSFrançois Tigeot } 14045718399fSFrançois Tigeot 1405ce3d36d7SFrançois Tigeot static bool 1406ce3d36d7SFrançois Tigeot mode_is_rb(const struct drm_display_mode *mode) 1407ce3d36d7SFrançois Tigeot { 1408ce3d36d7SFrançois Tigeot return (mode->htotal - mode->hdisplay == 160) && 1409ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hdisplay == 80) && 1410ce3d36d7SFrançois Tigeot (mode->hsync_end - mode->hsync_start == 32) && 1411ce3d36d7SFrançois Tigeot (mode->vsync_start - mode->vdisplay == 3); 1412ce3d36d7SFrançois Tigeot } 1413ce3d36d7SFrançois Tigeot 1414af4b81b9SFrançois Tigeot /* 1415af4b81b9SFrançois Tigeot * drm_mode_find_dmt - Create a copy of a mode if present in DMT 1416af4b81b9SFrançois Tigeot * @dev: Device to duplicate against 1417af4b81b9SFrançois Tigeot * @hsize: Mode width 1418af4b81b9SFrançois Tigeot * @vsize: Mode height 1419af4b81b9SFrançois Tigeot * @fresh: Mode refresh rate 1420af4b81b9SFrançois Tigeot * @rb: Mode reduced-blanking-ness 1421af4b81b9SFrançois Tigeot * 1422af4b81b9SFrançois Tigeot * Walk the DMT mode list looking for a match for the given parameters. 1423af4b81b9SFrançois Tigeot * Return a newly allocated copy of the mode, or NULL if not found. 1424af4b81b9SFrançois Tigeot */ 14255718399fSFrançois Tigeot struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, 1426ce3d36d7SFrançois Tigeot int hsize, int vsize, int fresh, 1427ce3d36d7SFrançois Tigeot bool rb) 14285718399fSFrançois Tigeot { 14295718399fSFrançois Tigeot int i; 14305718399fSFrançois Tigeot 1431d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 1432ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 1433ce3d36d7SFrançois Tigeot if (hsize != ptr->hdisplay) 1434ce3d36d7SFrançois Tigeot continue; 1435ce3d36d7SFrançois Tigeot if (vsize != ptr->vdisplay) 1436ce3d36d7SFrançois Tigeot continue; 1437ce3d36d7SFrançois Tigeot if (fresh != drm_mode_vrefresh(ptr)) 1438ce3d36d7SFrançois Tigeot continue; 1439ce3d36d7SFrançois Tigeot if (rb != mode_is_rb(ptr)) 1440ce3d36d7SFrançois Tigeot continue; 1441ce3d36d7SFrançois Tigeot 1442ce3d36d7SFrançois Tigeot return drm_mode_duplicate(dev, ptr); 14435718399fSFrançois Tigeot } 1444ce3d36d7SFrançois Tigeot 1445ce3d36d7SFrançois Tigeot return NULL; 14465718399fSFrançois Tigeot } 1447af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_find_dmt); 14485718399fSFrançois Tigeot 14495718399fSFrançois Tigeot typedef void detailed_cb(struct detailed_timing *timing, void *closure); 14505718399fSFrançois Tigeot 14515718399fSFrançois Tigeot static void 14525718399fSFrançois Tigeot cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 14535718399fSFrançois Tigeot { 14545718399fSFrançois Tigeot int i, n = 0; 1455e3b244c9SFrançois Tigeot u8 d = ext[0x02]; 14565718399fSFrançois Tigeot u8 *det_base = ext + d; 14575718399fSFrançois Tigeot 1458e3b244c9SFrançois Tigeot n = (127 - d) / 18; 14595718399fSFrançois Tigeot for (i = 0; i < n; i++) 14605718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 14615718399fSFrançois Tigeot } 14625718399fSFrançois Tigeot 14635718399fSFrançois Tigeot static void 14645718399fSFrançois Tigeot vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure) 14655718399fSFrançois Tigeot { 14665718399fSFrançois Tigeot unsigned int i, n = min((int)ext[0x02], 6); 14675718399fSFrançois Tigeot u8 *det_base = ext + 5; 14685718399fSFrançois Tigeot 14695718399fSFrançois Tigeot if (ext[0x01] != 1) 14705718399fSFrançois Tigeot return; /* unknown version */ 14715718399fSFrançois Tigeot 14725718399fSFrançois Tigeot for (i = 0; i < n; i++) 14735718399fSFrançois Tigeot cb((struct detailed_timing *)(det_base + 18 * i), closure); 14745718399fSFrançois Tigeot } 14755718399fSFrançois Tigeot 14765718399fSFrançois Tigeot static void 14775718399fSFrançois Tigeot drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) 14785718399fSFrançois Tigeot { 14795718399fSFrançois Tigeot int i; 14805718399fSFrançois Tigeot struct edid *edid = (struct edid *)raw_edid; 14815718399fSFrançois Tigeot 14825718399fSFrançois Tigeot if (edid == NULL) 14835718399fSFrançois Tigeot return; 14845718399fSFrançois Tigeot 14855718399fSFrançois Tigeot for (i = 0; i < EDID_DETAILED_TIMINGS; i++) 14865718399fSFrançois Tigeot cb(&(edid->detailed_timings[i]), closure); 14875718399fSFrançois Tigeot 14885718399fSFrançois Tigeot for (i = 1; i <= raw_edid[0x7e]; i++) { 14895718399fSFrançois Tigeot u8 *ext = raw_edid + (i * EDID_LENGTH); 14905718399fSFrançois Tigeot switch (*ext) { 14915718399fSFrançois Tigeot case CEA_EXT: 14925718399fSFrançois Tigeot cea_for_each_detailed_block(ext, cb, closure); 14935718399fSFrançois Tigeot break; 14945718399fSFrançois Tigeot case VTB_EXT: 14955718399fSFrançois Tigeot vtb_for_each_detailed_block(ext, cb, closure); 14965718399fSFrançois Tigeot break; 14975718399fSFrançois Tigeot default: 14985718399fSFrançois Tigeot break; 14995718399fSFrançois Tigeot } 15005718399fSFrançois Tigeot } 15015718399fSFrançois Tigeot } 15025718399fSFrançois Tigeot 15035718399fSFrançois Tigeot static void 15045718399fSFrançois Tigeot is_rb(struct detailed_timing *t, void *data) 15055718399fSFrançois Tigeot { 15065718399fSFrançois Tigeot u8 *r = (u8 *)t; 15075718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE) 15085718399fSFrançois Tigeot if (r[15] & 0x10) 15095718399fSFrançois Tigeot *(bool *)data = true; 15105718399fSFrançois Tigeot } 15115718399fSFrançois Tigeot 15125718399fSFrançois Tigeot /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ 15135718399fSFrançois Tigeot static bool 15145718399fSFrançois Tigeot drm_monitor_supports_rb(struct edid *edid) 15155718399fSFrançois Tigeot { 15165718399fSFrançois Tigeot if (edid->revision >= 4) { 15176e29dde8SFrançois Tigeot bool ret = false; 15185718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); 15195718399fSFrançois Tigeot return ret; 15205718399fSFrançois Tigeot } 15215718399fSFrançois Tigeot 15225718399fSFrançois Tigeot return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); 15235718399fSFrançois Tigeot } 15245718399fSFrançois Tigeot 15255718399fSFrançois Tigeot static void 15265718399fSFrançois Tigeot find_gtf2(struct detailed_timing *t, void *data) 15275718399fSFrançois Tigeot { 15285718399fSFrançois Tigeot u8 *r = (u8 *)t; 15295718399fSFrançois Tigeot if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02) 15305718399fSFrançois Tigeot *(u8 **)data = r; 15315718399fSFrançois Tigeot } 15325718399fSFrançois Tigeot 15335718399fSFrançois Tigeot /* Secondary GTF curve kicks in above some break frequency */ 15345718399fSFrançois Tigeot static int 15355718399fSFrançois Tigeot drm_gtf2_hbreak(struct edid *edid) 15365718399fSFrançois Tigeot { 15375718399fSFrançois Tigeot u8 *r = NULL; 15385718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15395718399fSFrançois Tigeot return r ? (r[12] * 2) : 0; 15405718399fSFrançois Tigeot } 15415718399fSFrançois Tigeot 15425718399fSFrançois Tigeot static int 15435718399fSFrançois Tigeot drm_gtf2_2c(struct edid *edid) 15445718399fSFrançois Tigeot { 15455718399fSFrançois Tigeot u8 *r = NULL; 15465718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15475718399fSFrançois Tigeot return r ? r[13] : 0; 15485718399fSFrançois Tigeot } 15495718399fSFrançois Tigeot 15505718399fSFrançois Tigeot static int 15515718399fSFrançois Tigeot drm_gtf2_m(struct edid *edid) 15525718399fSFrançois Tigeot { 15535718399fSFrançois Tigeot u8 *r = NULL; 15545718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15555718399fSFrançois Tigeot return r ? (r[15] << 8) + r[14] : 0; 15565718399fSFrançois Tigeot } 15575718399fSFrançois Tigeot 15585718399fSFrançois Tigeot static int 15595718399fSFrançois Tigeot drm_gtf2_k(struct edid *edid) 15605718399fSFrançois Tigeot { 15615718399fSFrançois Tigeot u8 *r = NULL; 15625718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15635718399fSFrançois Tigeot return r ? r[16] : 0; 15645718399fSFrançois Tigeot } 15655718399fSFrançois Tigeot 15665718399fSFrançois Tigeot static int 15675718399fSFrançois Tigeot drm_gtf2_2j(struct edid *edid) 15685718399fSFrançois Tigeot { 15695718399fSFrançois Tigeot u8 *r = NULL; 15705718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); 15715718399fSFrançois Tigeot return r ? r[17] : 0; 15725718399fSFrançois Tigeot } 15735718399fSFrançois Tigeot 15745718399fSFrançois Tigeot /** 15755718399fSFrançois Tigeot * standard_timing_level - get std. timing level(CVT/GTF/DMT) 15765718399fSFrançois Tigeot * @edid: EDID block to scan 15775718399fSFrançois Tigeot */ 15785718399fSFrançois Tigeot static int standard_timing_level(struct edid *edid) 15795718399fSFrançois Tigeot { 15805718399fSFrançois Tigeot if (edid->revision >= 2) { 15815718399fSFrançois Tigeot if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) 15825718399fSFrançois Tigeot return LEVEL_CVT; 15835718399fSFrançois Tigeot if (drm_gtf2_hbreak(edid)) 15845718399fSFrançois Tigeot return LEVEL_GTF2; 15855718399fSFrançois Tigeot return LEVEL_GTF; 15865718399fSFrançois Tigeot } 15875718399fSFrançois Tigeot return LEVEL_DMT; 15885718399fSFrançois Tigeot } 15895718399fSFrançois Tigeot 15905718399fSFrançois Tigeot /* 15915718399fSFrançois Tigeot * 0 is reserved. The spec says 0x01 fill for unused timings. Some old 15925718399fSFrançois Tigeot * monitors fill with ascii space (0x20) instead. 15935718399fSFrançois Tigeot */ 15945718399fSFrançois Tigeot static int 15955718399fSFrançois Tigeot bad_std_timing(u8 a, u8 b) 15965718399fSFrançois Tigeot { 15975718399fSFrançois Tigeot return (a == 0x00 && b == 0x00) || 15985718399fSFrançois Tigeot (a == 0x01 && b == 0x01) || 15995718399fSFrançois Tigeot (a == 0x20 && b == 0x20); 16005718399fSFrançois Tigeot } 16015718399fSFrançois Tigeot 16025718399fSFrançois Tigeot /** 16035718399fSFrançois Tigeot * drm_mode_std - convert standard mode info (width, height, refresh) into mode 16045718399fSFrançois Tigeot * @t: standard timing params 16055718399fSFrançois Tigeot * @timing_level: standard timing level 16065718399fSFrançois Tigeot * 16075718399fSFrançois Tigeot * Take the standard timing params (in this case width, aspect, and refresh) 16085718399fSFrançois Tigeot * and convert them into a real mode using CVT/GTF/DMT. 16095718399fSFrançois Tigeot */ 16105718399fSFrançois Tigeot static struct drm_display_mode * 16115718399fSFrançois Tigeot drm_mode_std(struct drm_connector *connector, struct edid *edid, 16125718399fSFrançois Tigeot struct std_timing *t, int revision) 16135718399fSFrançois Tigeot { 16145718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 16155718399fSFrançois Tigeot struct drm_display_mode *m, *mode = NULL; 16165718399fSFrançois Tigeot int hsize, vsize; 16175718399fSFrançois Tigeot int vrefresh_rate; 16185718399fSFrançois Tigeot unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) 16195718399fSFrançois Tigeot >> EDID_TIMING_ASPECT_SHIFT; 16205718399fSFrançois Tigeot unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) 16215718399fSFrançois Tigeot >> EDID_TIMING_VFREQ_SHIFT; 16225718399fSFrançois Tigeot int timing_level = standard_timing_level(edid); 16235718399fSFrançois Tigeot 16245718399fSFrançois Tigeot if (bad_std_timing(t->hsize, t->vfreq_aspect)) 16255718399fSFrançois Tigeot return NULL; 16265718399fSFrançois Tigeot 16275718399fSFrançois Tigeot /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ 16285718399fSFrançois Tigeot hsize = t->hsize * 8 + 248; 16295718399fSFrançois Tigeot /* vrefresh_rate = vfreq + 60 */ 16305718399fSFrançois Tigeot vrefresh_rate = vfreq + 60; 16315718399fSFrançois Tigeot /* the vdisplay is calculated based on the aspect ratio */ 16325718399fSFrançois Tigeot if (aspect_ratio == 0) { 16335718399fSFrançois Tigeot if (revision < 3) 16345718399fSFrançois Tigeot vsize = hsize; 16355718399fSFrançois Tigeot else 16365718399fSFrançois Tigeot vsize = (hsize * 10) / 16; 16375718399fSFrançois Tigeot } else if (aspect_ratio == 1) 16385718399fSFrançois Tigeot vsize = (hsize * 3) / 4; 16395718399fSFrançois Tigeot else if (aspect_ratio == 2) 16405718399fSFrançois Tigeot vsize = (hsize * 4) / 5; 16415718399fSFrançois Tigeot else 16425718399fSFrançois Tigeot vsize = (hsize * 9) / 16; 16435718399fSFrançois Tigeot 16445718399fSFrançois Tigeot /* HDTV hack, part 1 */ 16455718399fSFrançois Tigeot if (vrefresh_rate == 60 && 16465718399fSFrançois Tigeot ((hsize == 1360 && vsize == 765) || 16475718399fSFrançois Tigeot (hsize == 1368 && vsize == 769))) { 16485718399fSFrançois Tigeot hsize = 1366; 16495718399fSFrançois Tigeot vsize = 768; 16505718399fSFrançois Tigeot } 16515718399fSFrançois Tigeot 16525718399fSFrançois Tigeot /* 16535718399fSFrançois Tigeot * If this connector already has a mode for this size and refresh 16545718399fSFrançois Tigeot * rate (because it came from detailed or CVT info), use that 16555718399fSFrançois Tigeot * instead. This way we don't have to guess at interlace or 16565718399fSFrançois Tigeot * reduced blanking. 16575718399fSFrançois Tigeot */ 16585718399fSFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) 16595718399fSFrançois Tigeot if (m->hdisplay == hsize && m->vdisplay == vsize && 16605718399fSFrançois Tigeot drm_mode_vrefresh(m) == vrefresh_rate) 16615718399fSFrançois Tigeot return NULL; 16625718399fSFrançois Tigeot 16635718399fSFrançois Tigeot /* HDTV hack, part 2 */ 16645718399fSFrançois Tigeot if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) { 16655718399fSFrançois Tigeot mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0, 16665718399fSFrançois Tigeot false); 16675718399fSFrançois Tigeot mode->hdisplay = 1366; 16685718399fSFrançois Tigeot mode->hsync_start = mode->hsync_start - 1; 16695718399fSFrançois Tigeot mode->hsync_end = mode->hsync_end - 1; 16705718399fSFrançois Tigeot return mode; 16715718399fSFrançois Tigeot } 16725718399fSFrançois Tigeot 16735718399fSFrançois Tigeot /* check whether it can be found in default mode table */ 1674ce3d36d7SFrançois Tigeot if (drm_monitor_supports_rb(edid)) { 1675ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, 1676ce3d36d7SFrançois Tigeot true); 1677ce3d36d7SFrançois Tigeot if (mode) 1678ce3d36d7SFrançois Tigeot return mode; 1679ce3d36d7SFrançois Tigeot } 1680ce3d36d7SFrançois Tigeot mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false); 16815718399fSFrançois Tigeot if (mode) 16825718399fSFrançois Tigeot return mode; 16835718399fSFrançois Tigeot 1684ce3d36d7SFrançois Tigeot /* okay, generate it */ 16855718399fSFrançois Tigeot switch (timing_level) { 16865718399fSFrançois Tigeot case LEVEL_DMT: 16875718399fSFrançois Tigeot break; 16885718399fSFrançois Tigeot case LEVEL_GTF: 16895718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 16905718399fSFrançois Tigeot break; 16915718399fSFrançois Tigeot case LEVEL_GTF2: 16925718399fSFrançois Tigeot /* 16935718399fSFrançois Tigeot * This is potentially wrong if there's ever a monitor with 16945718399fSFrançois Tigeot * more than one ranges section, each claiming a different 16955718399fSFrançois Tigeot * secondary GTF curve. Please don't do that. 16965718399fSFrançois Tigeot */ 16975718399fSFrançois Tigeot mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); 1698ce3d36d7SFrançois Tigeot if (!mode) 1699ce3d36d7SFrançois Tigeot return NULL; 17005718399fSFrançois Tigeot if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { 1701ce3d36d7SFrançois Tigeot drm_mode_destroy(dev, mode); 17025718399fSFrançois Tigeot mode = drm_gtf_mode_complex(dev, hsize, vsize, 17035718399fSFrançois Tigeot vrefresh_rate, 0, 0, 17045718399fSFrançois Tigeot drm_gtf2_m(edid), 17055718399fSFrançois Tigeot drm_gtf2_2c(edid), 17065718399fSFrançois Tigeot drm_gtf2_k(edid), 17075718399fSFrançois Tigeot drm_gtf2_2j(edid)); 17085718399fSFrançois Tigeot } 17095718399fSFrançois Tigeot break; 17105718399fSFrançois Tigeot case LEVEL_CVT: 17115718399fSFrançois Tigeot mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, 17125718399fSFrançois Tigeot false); 17135718399fSFrançois Tigeot break; 17145718399fSFrançois Tigeot } 17155718399fSFrançois Tigeot return mode; 17165718399fSFrançois Tigeot } 17175718399fSFrançois Tigeot 17185718399fSFrançois Tigeot /* 17195718399fSFrançois Tigeot * EDID is delightfully ambiguous about how interlaced modes are to be 17205718399fSFrançois Tigeot * encoded. Our internal representation is of frame height, but some 17215718399fSFrançois Tigeot * HDTV detailed timings are encoded as field height. 17225718399fSFrançois Tigeot * 17235718399fSFrançois Tigeot * The format list here is from CEA, in frame size. Technically we 17245718399fSFrançois Tigeot * should be checking refresh rate too. Whatever. 17255718399fSFrançois Tigeot */ 17265718399fSFrançois Tigeot static void 17275718399fSFrançois Tigeot drm_mode_do_interlace_quirk(struct drm_display_mode *mode, 17285718399fSFrançois Tigeot struct detailed_pixel_timing *pt) 17295718399fSFrançois Tigeot { 17305718399fSFrançois Tigeot int i; 17315718399fSFrançois Tigeot static const struct { 17325718399fSFrançois Tigeot int w, h; 17335718399fSFrançois Tigeot } cea_interlaced[] = { 17345718399fSFrançois Tigeot { 1920, 1080 }, 17355718399fSFrançois Tigeot { 720, 480 }, 17365718399fSFrançois Tigeot { 1440, 480 }, 17375718399fSFrançois Tigeot { 2880, 480 }, 17385718399fSFrançois Tigeot { 720, 576 }, 17395718399fSFrançois Tigeot { 1440, 576 }, 17405718399fSFrançois Tigeot { 2880, 576 }, 17415718399fSFrançois Tigeot }; 17425718399fSFrançois Tigeot 17435718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_INTERLACED)) 17445718399fSFrançois Tigeot return; 17455718399fSFrançois Tigeot 17466e29dde8SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) { 17475718399fSFrançois Tigeot if ((mode->hdisplay == cea_interlaced[i].w) && 17485718399fSFrançois Tigeot (mode->vdisplay == cea_interlaced[i].h / 2)) { 17495718399fSFrançois Tigeot mode->vdisplay *= 2; 17505718399fSFrançois Tigeot mode->vsync_start *= 2; 17515718399fSFrançois Tigeot mode->vsync_end *= 2; 17525718399fSFrançois Tigeot mode->vtotal *= 2; 17535718399fSFrançois Tigeot mode->vtotal |= 1; 17545718399fSFrançois Tigeot } 17555718399fSFrançois Tigeot } 17565718399fSFrançois Tigeot 17575718399fSFrançois Tigeot mode->flags |= DRM_MODE_FLAG_INTERLACE; 17585718399fSFrançois Tigeot } 17595718399fSFrançois Tigeot 17605718399fSFrançois Tigeot /** 17615718399fSFrançois Tigeot * drm_mode_detailed - create a new mode from an EDID detailed timing section 17625718399fSFrançois Tigeot * @dev: DRM device (needed to create new mode) 17635718399fSFrançois Tigeot * @edid: EDID block 17645718399fSFrançois Tigeot * @timing: EDID detailed timing info 17655718399fSFrançois Tigeot * @quirks: quirks to apply 17665718399fSFrançois Tigeot * 17675718399fSFrançois Tigeot * An EDID detailed timing block contains enough info for us to create and 17685718399fSFrançois Tigeot * return a new struct drm_display_mode. 17695718399fSFrançois Tigeot */ 17705718399fSFrançois Tigeot static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, 17715718399fSFrançois Tigeot struct edid *edid, 17725718399fSFrançois Tigeot struct detailed_timing *timing, 17735718399fSFrançois Tigeot u32 quirks) 17745718399fSFrançois Tigeot { 17755718399fSFrançois Tigeot struct drm_display_mode *mode; 17765718399fSFrançois Tigeot struct detailed_pixel_timing *pt = &timing->data.pixel_data; 17775718399fSFrançois Tigeot unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; 17785718399fSFrançois Tigeot unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; 17795718399fSFrançois Tigeot unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; 17805718399fSFrançois Tigeot unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; 17815718399fSFrançois Tigeot unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; 17825718399fSFrançois Tigeot unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; 1783d82bf20eSFrançois Tigeot unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4; 17845718399fSFrançois Tigeot unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); 17855718399fSFrançois Tigeot 17865718399fSFrançois Tigeot /* ignore tiny modes */ 17875718399fSFrançois Tigeot if (hactive < 64 || vactive < 64) 17885718399fSFrançois Tigeot return NULL; 17895718399fSFrançois Tigeot 17905718399fSFrançois Tigeot if (pt->misc & DRM_EDID_PT_STEREO) { 1791*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("stereo mode not supported\n"); 17925718399fSFrançois Tigeot return NULL; 17935718399fSFrançois Tigeot } 17945718399fSFrançois Tigeot if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { 1795*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("composite sync not supported\n"); 17965718399fSFrançois Tigeot } 17975718399fSFrançois Tigeot 17985718399fSFrançois Tigeot /* it is incorrect if hsync/vsync width is zero */ 17995718399fSFrançois Tigeot if (!hsync_pulse_width || !vsync_pulse_width) { 18005718399fSFrançois Tigeot DRM_DEBUG_KMS("Incorrect Detailed timing. " 18015718399fSFrançois Tigeot "Wrong Hsync/Vsync pulse width\n"); 18025718399fSFrançois Tigeot return NULL; 18035718399fSFrançois Tigeot } 18046e29dde8SFrançois Tigeot 18056e29dde8SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { 18066e29dde8SFrançois Tigeot mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); 18076e29dde8SFrançois Tigeot if (!mode) 18086e29dde8SFrançois Tigeot return NULL; 18096e29dde8SFrançois Tigeot 18106e29dde8SFrançois Tigeot goto set_size; 18116e29dde8SFrançois Tigeot } 18126e29dde8SFrançois Tigeot 18135718399fSFrançois Tigeot mode = drm_mode_create(dev); 18145718399fSFrançois Tigeot if (!mode) 18155718399fSFrançois Tigeot return NULL; 18165718399fSFrançois Tigeot 18175718399fSFrançois Tigeot if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) 18186e29dde8SFrançois Tigeot timing->pixel_clock = cpu_to_le16(1088); 18195718399fSFrançois Tigeot 18206e29dde8SFrançois Tigeot mode->clock = le16_to_cpu(timing->pixel_clock) * 10; 18215718399fSFrançois Tigeot 18225718399fSFrançois Tigeot mode->hdisplay = hactive; 18235718399fSFrançois Tigeot mode->hsync_start = mode->hdisplay + hsync_offset; 18245718399fSFrançois Tigeot mode->hsync_end = mode->hsync_start + hsync_pulse_width; 18255718399fSFrançois Tigeot mode->htotal = mode->hdisplay + hblank; 18265718399fSFrançois Tigeot 18275718399fSFrançois Tigeot mode->vdisplay = vactive; 18285718399fSFrançois Tigeot mode->vsync_start = mode->vdisplay + vsync_offset; 18295718399fSFrançois Tigeot mode->vsync_end = mode->vsync_start + vsync_pulse_width; 18305718399fSFrançois Tigeot mode->vtotal = mode->vdisplay + vblank; 18315718399fSFrançois Tigeot 18325718399fSFrançois Tigeot /* Some EDIDs have bogus h/vtotal values */ 18335718399fSFrançois Tigeot if (mode->hsync_end > mode->htotal) 18345718399fSFrançois Tigeot mode->htotal = mode->hsync_end + 1; 18355718399fSFrançois Tigeot if (mode->vsync_end > mode->vtotal) 18365718399fSFrançois Tigeot mode->vtotal = mode->vsync_end + 1; 18375718399fSFrançois Tigeot 18385718399fSFrançois Tigeot drm_mode_do_interlace_quirk(mode, pt); 18395718399fSFrançois Tigeot 18405718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { 18415718399fSFrançois Tigeot pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; 18425718399fSFrançois Tigeot } 18435718399fSFrançois Tigeot 18445718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? 18455718399fSFrançois Tigeot DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 18465718399fSFrançois Tigeot mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? 18475718399fSFrançois Tigeot DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 18485718399fSFrançois Tigeot 18496e29dde8SFrançois Tigeot set_size: 18505718399fSFrançois Tigeot mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; 18515718399fSFrançois Tigeot mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; 18525718399fSFrançois Tigeot 18535718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_IN_CM) { 18545718399fSFrançois Tigeot mode->width_mm *= 10; 18555718399fSFrançois Tigeot mode->height_mm *= 10; 18565718399fSFrançois Tigeot } 18575718399fSFrançois Tigeot 18585718399fSFrançois Tigeot if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { 18595718399fSFrançois Tigeot mode->width_mm = edid->width_cm * 10; 18605718399fSFrançois Tigeot mode->height_mm = edid->height_cm * 10; 18615718399fSFrançois Tigeot } 18625718399fSFrançois Tigeot 18636e29dde8SFrançois Tigeot mode->type = DRM_MODE_TYPE_DRIVER; 1864d82bf20eSFrançois Tigeot mode->vrefresh = drm_mode_vrefresh(mode); 18656e29dde8SFrançois Tigeot drm_mode_set_name(mode); 18666e29dde8SFrançois Tigeot 18675718399fSFrançois Tigeot return mode; 18685718399fSFrançois Tigeot } 18695718399fSFrançois Tigeot 18705718399fSFrançois Tigeot static bool 1871ce3d36d7SFrançois Tigeot mode_in_hsync_range(const struct drm_display_mode *mode, 18725718399fSFrançois Tigeot struct edid *edid, u8 *t) 18735718399fSFrançois Tigeot { 18745718399fSFrançois Tigeot int hsync, hmin, hmax; 18755718399fSFrançois Tigeot 18765718399fSFrançois Tigeot hmin = t[7]; 18775718399fSFrançois Tigeot if (edid->revision >= 4) 18785718399fSFrançois Tigeot hmin += ((t[4] & 0x04) ? 255 : 0); 18795718399fSFrançois Tigeot hmax = t[8]; 18805718399fSFrançois Tigeot if (edid->revision >= 4) 18815718399fSFrançois Tigeot hmax += ((t[4] & 0x08) ? 255 : 0); 18825718399fSFrançois Tigeot hsync = drm_mode_hsync(mode); 18835718399fSFrançois Tigeot 18845718399fSFrançois Tigeot return (hsync <= hmax && hsync >= hmin); 18855718399fSFrançois Tigeot } 18865718399fSFrançois Tigeot 18875718399fSFrançois Tigeot static bool 1888ce3d36d7SFrançois Tigeot mode_in_vsync_range(const struct drm_display_mode *mode, 18895718399fSFrançois Tigeot struct edid *edid, u8 *t) 18905718399fSFrançois Tigeot { 18915718399fSFrançois Tigeot int vsync, vmin, vmax; 18925718399fSFrançois Tigeot 18935718399fSFrançois Tigeot vmin = t[5]; 18945718399fSFrançois Tigeot if (edid->revision >= 4) 18955718399fSFrançois Tigeot vmin += ((t[4] & 0x01) ? 255 : 0); 18965718399fSFrançois Tigeot vmax = t[6]; 18975718399fSFrançois Tigeot if (edid->revision >= 4) 18985718399fSFrançois Tigeot vmax += ((t[4] & 0x02) ? 255 : 0); 18995718399fSFrançois Tigeot vsync = drm_mode_vrefresh(mode); 19005718399fSFrançois Tigeot 19015718399fSFrançois Tigeot return (vsync <= vmax && vsync >= vmin); 19025718399fSFrançois Tigeot } 19035718399fSFrançois Tigeot 19045718399fSFrançois Tigeot static u32 19055718399fSFrançois Tigeot range_pixel_clock(struct edid *edid, u8 *t) 19065718399fSFrançois Tigeot { 19075718399fSFrançois Tigeot /* unspecified */ 19085718399fSFrançois Tigeot if (t[9] == 0 || t[9] == 255) 19095718399fSFrançois Tigeot return 0; 19105718399fSFrançois Tigeot 19115718399fSFrançois Tigeot /* 1.4 with CVT support gives us real precision, yay */ 19125718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 19135718399fSFrançois Tigeot return (t[9] * 10000) - ((t[12] >> 2) * 250); 19145718399fSFrançois Tigeot 19155718399fSFrançois Tigeot /* 1.3 is pathetic, so fuzz up a bit */ 19165718399fSFrançois Tigeot return t[9] * 10000 + 5001; 19175718399fSFrançois Tigeot } 19185718399fSFrançois Tigeot 19195718399fSFrançois Tigeot static bool 1920ce3d36d7SFrançois Tigeot mode_in_range(const struct drm_display_mode *mode, struct edid *edid, 19215718399fSFrançois Tigeot struct detailed_timing *timing) 19225718399fSFrançois Tigeot { 19235718399fSFrançois Tigeot u32 max_clock; 19245718399fSFrançois Tigeot u8 *t = (u8 *)timing; 19255718399fSFrançois Tigeot 19265718399fSFrançois Tigeot if (!mode_in_hsync_range(mode, edid, t)) 19275718399fSFrançois Tigeot return false; 19285718399fSFrançois Tigeot 19295718399fSFrançois Tigeot if (!mode_in_vsync_range(mode, edid, t)) 19305718399fSFrançois Tigeot return false; 19315718399fSFrançois Tigeot 19325718399fSFrançois Tigeot if ((max_clock = range_pixel_clock(edid, t))) 19335718399fSFrançois Tigeot if (mode->clock > max_clock) 19345718399fSFrançois Tigeot return false; 19355718399fSFrançois Tigeot 19365718399fSFrançois Tigeot /* 1.4 max horizontal check */ 19375718399fSFrançois Tigeot if (edid->revision >= 4 && t[10] == 0x04) 19385718399fSFrançois Tigeot if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) 19395718399fSFrançois Tigeot return false; 19405718399fSFrançois Tigeot 19415718399fSFrançois Tigeot if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) 19425718399fSFrançois Tigeot return false; 19435718399fSFrançois Tigeot 19445718399fSFrançois Tigeot return true; 19455718399fSFrançois Tigeot } 19465718399fSFrançois Tigeot 19476e29dde8SFrançois Tigeot static bool valid_inferred_mode(const struct drm_connector *connector, 19486e29dde8SFrançois Tigeot const struct drm_display_mode *mode) 19496e29dde8SFrançois Tigeot { 19506e29dde8SFrançois Tigeot struct drm_display_mode *m; 19516e29dde8SFrançois Tigeot bool ok = false; 19526e29dde8SFrançois Tigeot 19536e29dde8SFrançois Tigeot list_for_each_entry(m, &connector->probed_modes, head) { 19546e29dde8SFrançois Tigeot if (mode->hdisplay == m->hdisplay && 19556e29dde8SFrançois Tigeot mode->vdisplay == m->vdisplay && 19566e29dde8SFrançois Tigeot drm_mode_vrefresh(mode) == drm_mode_vrefresh(m)) 19576e29dde8SFrançois Tigeot return false; /* duplicated */ 19586e29dde8SFrançois Tigeot if (mode->hdisplay <= m->hdisplay && 19596e29dde8SFrançois Tigeot mode->vdisplay <= m->vdisplay) 19606e29dde8SFrançois Tigeot ok = true; 19616e29dde8SFrançois Tigeot } 19626e29dde8SFrançois Tigeot return ok; 19636e29dde8SFrançois Tigeot } 19646e29dde8SFrançois Tigeot 19655718399fSFrançois Tigeot static int 19666e29dde8SFrançois Tigeot drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, 19675718399fSFrançois Tigeot struct detailed_timing *timing) 19685718399fSFrançois Tigeot { 19695718399fSFrançois Tigeot int i, modes = 0; 19705718399fSFrançois Tigeot struct drm_display_mode *newmode; 19715718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 19725718399fSFrançois Tigeot 1973d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { 19746e29dde8SFrançois Tigeot if (mode_in_range(drm_dmt_modes + i, edid, timing) && 19756e29dde8SFrançois Tigeot valid_inferred_mode(connector, drm_dmt_modes + i)) { 19765718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); 19775718399fSFrançois Tigeot if (newmode) { 19785718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 19795718399fSFrançois Tigeot modes++; 19805718399fSFrançois Tigeot } 19815718399fSFrançois Tigeot } 19825718399fSFrançois Tigeot } 19835718399fSFrançois Tigeot 19845718399fSFrançois Tigeot return modes; 19855718399fSFrançois Tigeot } 19865718399fSFrançois Tigeot 19876e29dde8SFrançois Tigeot /* fix up 1366x768 mode from 1368x768; 19886e29dde8SFrançois Tigeot * GFT/CVT can't express 1366 width which isn't dividable by 8 19896e29dde8SFrançois Tigeot */ 19906e29dde8SFrançois Tigeot static void fixup_mode_1366x768(struct drm_display_mode *mode) 19916e29dde8SFrançois Tigeot { 19926e29dde8SFrançois Tigeot if (mode->hdisplay == 1368 && mode->vdisplay == 768) { 19936e29dde8SFrançois Tigeot mode->hdisplay = 1366; 19946e29dde8SFrançois Tigeot mode->hsync_start--; 19956e29dde8SFrançois Tigeot mode->hsync_end--; 19966e29dde8SFrançois Tigeot drm_mode_set_name(mode); 19976e29dde8SFrançois Tigeot } 19986e29dde8SFrançois Tigeot } 19996e29dde8SFrançois Tigeot 20006e29dde8SFrançois Tigeot static int 20016e29dde8SFrançois Tigeot drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, 20026e29dde8SFrançois Tigeot struct detailed_timing *timing) 20036e29dde8SFrançois Tigeot { 20046e29dde8SFrançois Tigeot int i, modes = 0; 20056e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 20066e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 20076e29dde8SFrançois Tigeot 2008d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 20096e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 20106e29dde8SFrançois Tigeot newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0); 20116e29dde8SFrançois Tigeot if (!newmode) 20126e29dde8SFrançois Tigeot return modes; 20136e29dde8SFrançois Tigeot 20146e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 20156e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 20166e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 20176e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 20186e29dde8SFrançois Tigeot continue; 20196e29dde8SFrançois Tigeot } 20206e29dde8SFrançois Tigeot 20216e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 20226e29dde8SFrançois Tigeot modes++; 20236e29dde8SFrançois Tigeot } 20246e29dde8SFrançois Tigeot 20256e29dde8SFrançois Tigeot return modes; 20266e29dde8SFrançois Tigeot } 20276e29dde8SFrançois Tigeot 20286e29dde8SFrançois Tigeot static int 20296e29dde8SFrançois Tigeot drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, 20306e29dde8SFrançois Tigeot struct detailed_timing *timing) 20316e29dde8SFrançois Tigeot { 20326e29dde8SFrançois Tigeot int i, modes = 0; 20336e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 20346e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 20356e29dde8SFrançois Tigeot bool rb = drm_monitor_supports_rb(edid); 20366e29dde8SFrançois Tigeot 2037d82bf20eSFrançois Tigeot for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { 20386e29dde8SFrançois Tigeot const struct minimode *m = &extra_modes[i]; 20396e29dde8SFrançois Tigeot newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0); 20406e29dde8SFrançois Tigeot if (!newmode) 20416e29dde8SFrançois Tigeot return modes; 20426e29dde8SFrançois Tigeot 20436e29dde8SFrançois Tigeot fixup_mode_1366x768(newmode); 20446e29dde8SFrançois Tigeot if (!mode_in_range(newmode, edid, timing) || 20456e29dde8SFrançois Tigeot !valid_inferred_mode(connector, newmode)) { 20466e29dde8SFrançois Tigeot drm_mode_destroy(dev, newmode); 20476e29dde8SFrançois Tigeot continue; 20486e29dde8SFrançois Tigeot } 20496e29dde8SFrançois Tigeot 20506e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 20516e29dde8SFrançois Tigeot modes++; 20526e29dde8SFrançois Tigeot } 20536e29dde8SFrançois Tigeot 20546e29dde8SFrançois Tigeot return modes; 20556e29dde8SFrançois Tigeot } 20566e29dde8SFrançois Tigeot 20575718399fSFrançois Tigeot static void 20585718399fSFrançois Tigeot do_inferred_modes(struct detailed_timing *timing, void *c) 20595718399fSFrançois Tigeot { 20605718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 20615718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 20626e29dde8SFrançois Tigeot struct detailed_data_monitor_range *range = &data->data.range; 20635718399fSFrançois Tigeot 20646e29dde8SFrançois Tigeot if (data->type != EDID_DETAIL_MONITOR_RANGE) 20656e29dde8SFrançois Tigeot return; 20666e29dde8SFrançois Tigeot 20676e29dde8SFrançois Tigeot closure->modes += drm_dmt_modes_for_range(closure->connector, 20686e29dde8SFrançois Tigeot closure->edid, 20696e29dde8SFrançois Tigeot timing); 20706e29dde8SFrançois Tigeot 20716e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 1)) 20726e29dde8SFrançois Tigeot return; /* GTF not defined yet */ 20736e29dde8SFrançois Tigeot 20746e29dde8SFrançois Tigeot switch (range->flags) { 20756e29dde8SFrançois Tigeot case 0x02: /* secondary gtf, XXX could do more */ 20766e29dde8SFrançois Tigeot case 0x00: /* default gtf */ 20775718399fSFrançois Tigeot closure->modes += drm_gtf_modes_for_range(closure->connector, 20785718399fSFrançois Tigeot closure->edid, 20795718399fSFrançois Tigeot timing); 20806e29dde8SFrançois Tigeot break; 20816e29dde8SFrançois Tigeot case 0x04: /* cvt, only in 1.4+ */ 20826e29dde8SFrançois Tigeot if (!version_greater(closure->edid, 1, 3)) 20836e29dde8SFrançois Tigeot break; 20846e29dde8SFrançois Tigeot 20856e29dde8SFrançois Tigeot closure->modes += drm_cvt_modes_for_range(closure->connector, 20866e29dde8SFrançois Tigeot closure->edid, 20876e29dde8SFrançois Tigeot timing); 20886e29dde8SFrançois Tigeot break; 20896e29dde8SFrançois Tigeot case 0x01: /* just the ranges, no formula */ 20906e29dde8SFrançois Tigeot default: 20916e29dde8SFrançois Tigeot break; 20926e29dde8SFrançois Tigeot } 20935718399fSFrançois Tigeot } 20945718399fSFrançois Tigeot 20955718399fSFrançois Tigeot static int 20965718399fSFrançois Tigeot add_inferred_modes(struct drm_connector *connector, struct edid *edid) 20975718399fSFrançois Tigeot { 20985718399fSFrançois Tigeot struct detailed_mode_closure closure = { 20995718399fSFrançois Tigeot connector, edid, 0, 0, 0 21005718399fSFrançois Tigeot }; 21015718399fSFrançois Tigeot 21025718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 21035718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, 21045718399fSFrançois Tigeot &closure); 21055718399fSFrançois Tigeot 21065718399fSFrançois Tigeot return closure.modes; 21075718399fSFrançois Tigeot } 21085718399fSFrançois Tigeot 21095718399fSFrançois Tigeot static int 21105718399fSFrançois Tigeot drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) 21115718399fSFrançois Tigeot { 21125718399fSFrançois Tigeot int i, j, m, modes = 0; 21135718399fSFrançois Tigeot struct drm_display_mode *mode; 21145718399fSFrançois Tigeot u8 *est = ((u8 *)timing) + 5; 21155718399fSFrançois Tigeot 21165718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 2117*9edbd4a0SFrançois Tigeot for (j = 7; j >= 0; j--) { 21185718399fSFrançois Tigeot m = (i * 8) + (7 - j); 2119ce3d36d7SFrançois Tigeot if (m >= ARRAY_SIZE(est3_modes)) 21205718399fSFrançois Tigeot break; 21215718399fSFrançois Tigeot if (est[i] & (1 << j)) { 21225718399fSFrançois Tigeot mode = drm_mode_find_dmt(connector->dev, 21235718399fSFrançois Tigeot est3_modes[m].w, 21245718399fSFrançois Tigeot est3_modes[m].h, 2125ce3d36d7SFrançois Tigeot est3_modes[m].r, 2126ce3d36d7SFrançois Tigeot est3_modes[m].rb); 21275718399fSFrançois Tigeot if (mode) { 21285718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 21295718399fSFrançois Tigeot modes++; 21305718399fSFrançois Tigeot } 21315718399fSFrançois Tigeot } 21325718399fSFrançois Tigeot } 21335718399fSFrançois Tigeot } 21345718399fSFrançois Tigeot 21355718399fSFrançois Tigeot return modes; 21365718399fSFrançois Tigeot } 21375718399fSFrançois Tigeot 21385718399fSFrançois Tigeot static void 21395718399fSFrançois Tigeot do_established_modes(struct detailed_timing *timing, void *c) 21405718399fSFrançois Tigeot { 21415718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 21425718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 21435718399fSFrançois Tigeot 21445718399fSFrançois Tigeot if (data->type == EDID_DETAIL_EST_TIMINGS) 21455718399fSFrançois Tigeot closure->modes += drm_est3_modes(closure->connector, timing); 21465718399fSFrançois Tigeot } 21475718399fSFrançois Tigeot 21485718399fSFrançois Tigeot /** 21495718399fSFrançois Tigeot * add_established_modes - get est. modes from EDID and add them 21505718399fSFrançois Tigeot * @edid: EDID block to scan 21515718399fSFrançois Tigeot * 21525718399fSFrançois Tigeot * Each EDID block contains a bitmap of the supported "established modes" list 21535718399fSFrançois Tigeot * (defined above). Tease them out and add them to the global modes list. 21545718399fSFrançois Tigeot */ 21555718399fSFrançois Tigeot static int 21565718399fSFrançois Tigeot add_established_modes(struct drm_connector *connector, struct edid *edid) 21575718399fSFrançois Tigeot { 21585718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 21595718399fSFrançois Tigeot unsigned long est_bits = edid->established_timings.t1 | 21605718399fSFrançois Tigeot (edid->established_timings.t2 << 8) | 21615718399fSFrançois Tigeot ((edid->established_timings.mfg_rsvd & 0x80) << 9); 21625718399fSFrançois Tigeot int i, modes = 0; 21635718399fSFrançois Tigeot struct detailed_mode_closure closure = { 21645718399fSFrançois Tigeot connector, edid, 0, 0, 0 21655718399fSFrançois Tigeot }; 21665718399fSFrançois Tigeot 21675718399fSFrançois Tigeot for (i = 0; i <= EDID_EST_TIMINGS; i++) { 21685718399fSFrançois Tigeot if (est_bits & (1<<i)) { 21695718399fSFrançois Tigeot struct drm_display_mode *newmode; 21705718399fSFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); 21715718399fSFrançois Tigeot if (newmode) { 21725718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 21735718399fSFrançois Tigeot modes++; 21745718399fSFrançois Tigeot } 21755718399fSFrançois Tigeot } 21765718399fSFrançois Tigeot } 21775718399fSFrançois Tigeot 21785718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 21795718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, 21805718399fSFrançois Tigeot do_established_modes, &closure); 21815718399fSFrançois Tigeot 21825718399fSFrançois Tigeot return modes + closure.modes; 21835718399fSFrançois Tigeot } 21845718399fSFrançois Tigeot 21855718399fSFrançois Tigeot static void 21865718399fSFrançois Tigeot do_standard_modes(struct detailed_timing *timing, void *c) 21875718399fSFrançois Tigeot { 21885718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 21895718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 21905718399fSFrançois Tigeot struct drm_connector *connector = closure->connector; 21915718399fSFrançois Tigeot struct edid *edid = closure->edid; 21925718399fSFrançois Tigeot 21935718399fSFrançois Tigeot if (data->type == EDID_DETAIL_STD_MODES) { 21945718399fSFrançois Tigeot int i; 21955718399fSFrançois Tigeot for (i = 0; i < 6; i++) { 21965718399fSFrançois Tigeot struct std_timing *std; 21975718399fSFrançois Tigeot struct drm_display_mode *newmode; 21985718399fSFrançois Tigeot 21995718399fSFrançois Tigeot std = &data->data.timings[i]; 22005718399fSFrançois Tigeot newmode = drm_mode_std(connector, edid, std, 22015718399fSFrançois Tigeot edid->revision); 22025718399fSFrançois Tigeot if (newmode) { 22035718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22045718399fSFrançois Tigeot closure->modes++; 22055718399fSFrançois Tigeot } 22065718399fSFrançois Tigeot } 22075718399fSFrançois Tigeot } 22085718399fSFrançois Tigeot } 22095718399fSFrançois Tigeot 22105718399fSFrançois Tigeot /** 22115718399fSFrançois Tigeot * add_standard_modes - get std. modes from EDID and add them 22125718399fSFrançois Tigeot * @edid: EDID block to scan 22135718399fSFrançois Tigeot * 22145718399fSFrançois Tigeot * Standard modes can be calculated using the appropriate standard (DMT, 22155718399fSFrançois Tigeot * GTF or CVT. Grab them from @edid and add them to the list. 22165718399fSFrançois Tigeot */ 22175718399fSFrançois Tigeot static int 22185718399fSFrançois Tigeot add_standard_modes(struct drm_connector *connector, struct edid *edid) 22195718399fSFrançois Tigeot { 22205718399fSFrançois Tigeot int i, modes = 0; 22215718399fSFrançois Tigeot struct detailed_mode_closure closure = { 22225718399fSFrançois Tigeot connector, edid, 0, 0, 0 22235718399fSFrançois Tigeot }; 22245718399fSFrançois Tigeot 22255718399fSFrançois Tigeot for (i = 0; i < EDID_STD_TIMINGS; i++) { 22265718399fSFrançois Tigeot struct drm_display_mode *newmode; 22275718399fSFrançois Tigeot 22285718399fSFrançois Tigeot newmode = drm_mode_std(connector, edid, 22295718399fSFrançois Tigeot &edid->standard_timings[i], 22305718399fSFrançois Tigeot edid->revision); 22315718399fSFrançois Tigeot if (newmode) { 22325718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22335718399fSFrançois Tigeot modes++; 22345718399fSFrançois Tigeot } 22355718399fSFrançois Tigeot } 22365718399fSFrançois Tigeot 22375718399fSFrançois Tigeot if (version_greater(edid, 1, 0)) 22385718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_standard_modes, 22395718399fSFrançois Tigeot &closure); 22405718399fSFrançois Tigeot 22415718399fSFrançois Tigeot /* XXX should also look for standard codes in VTB blocks */ 22425718399fSFrançois Tigeot 22435718399fSFrançois Tigeot return modes + closure.modes; 22445718399fSFrançois Tigeot } 22455718399fSFrançois Tigeot 22465718399fSFrançois Tigeot static int drm_cvt_modes(struct drm_connector *connector, 22475718399fSFrançois Tigeot struct detailed_timing *timing) 22485718399fSFrançois Tigeot { 22495718399fSFrançois Tigeot int i, j, modes = 0; 22505718399fSFrançois Tigeot struct drm_display_mode *newmode; 22515718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 22525718399fSFrançois Tigeot struct cvt_timing *cvt; 22535718399fSFrançois Tigeot const int rates[] = { 60, 85, 75, 60, 50 }; 22545718399fSFrançois Tigeot const u8 empty[3] = { 0, 0, 0 }; 22555718399fSFrançois Tigeot 22565718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 2257a3faafcbSSascha Wildner int width = 0, height; 22585718399fSFrançois Tigeot cvt = &(timing->data.other_data.data.cvt[i]); 22595718399fSFrançois Tigeot 22605718399fSFrançois Tigeot if (!memcmp(cvt->code, empty, 3)) 22615718399fSFrançois Tigeot continue; 22625718399fSFrançois Tigeot 22635718399fSFrançois Tigeot height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2; 22645718399fSFrançois Tigeot switch (cvt->code[1] & 0x0c) { 22655718399fSFrançois Tigeot case 0x00: 22665718399fSFrançois Tigeot width = height * 4 / 3; 22675718399fSFrançois Tigeot break; 22685718399fSFrançois Tigeot case 0x04: 22695718399fSFrançois Tigeot width = height * 16 / 9; 22705718399fSFrançois Tigeot break; 22715718399fSFrançois Tigeot case 0x08: 22725718399fSFrançois Tigeot width = height * 16 / 10; 22735718399fSFrançois Tigeot break; 22745718399fSFrançois Tigeot case 0x0c: 22755718399fSFrançois Tigeot width = height * 15 / 9; 22765718399fSFrançois Tigeot break; 22775718399fSFrançois Tigeot } 22785718399fSFrançois Tigeot 22795718399fSFrançois Tigeot for (j = 1; j < 5; j++) { 22805718399fSFrançois Tigeot if (cvt->code[2] & (1 << j)) { 22815718399fSFrançois Tigeot newmode = drm_cvt_mode(dev, width, height, 22825718399fSFrançois Tigeot rates[j], j == 0, 22835718399fSFrançois Tigeot false, false); 22845718399fSFrançois Tigeot if (newmode) { 22855718399fSFrançois Tigeot drm_mode_probed_add(connector, newmode); 22865718399fSFrançois Tigeot modes++; 22875718399fSFrançois Tigeot } 22885718399fSFrançois Tigeot } 22895718399fSFrançois Tigeot } 22905718399fSFrançois Tigeot } 22915718399fSFrançois Tigeot 22925718399fSFrançois Tigeot return modes; 22935718399fSFrançois Tigeot } 22945718399fSFrançois Tigeot 22955718399fSFrançois Tigeot static void 22965718399fSFrançois Tigeot do_cvt_mode(struct detailed_timing *timing, void *c) 22975718399fSFrançois Tigeot { 22985718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 22995718399fSFrançois Tigeot struct detailed_non_pixel *data = &timing->data.other_data; 23005718399fSFrançois Tigeot 23015718399fSFrançois Tigeot if (data->type == EDID_DETAIL_CVT_3BYTE) 23025718399fSFrançois Tigeot closure->modes += drm_cvt_modes(closure->connector, timing); 23035718399fSFrançois Tigeot } 23045718399fSFrançois Tigeot 23055718399fSFrançois Tigeot static int 23065718399fSFrançois Tigeot add_cvt_modes(struct drm_connector *connector, struct edid *edid) 23075718399fSFrançois Tigeot { 23085718399fSFrançois Tigeot struct detailed_mode_closure closure = { 23095718399fSFrançois Tigeot connector, edid, 0, 0, 0 23105718399fSFrançois Tigeot }; 23115718399fSFrançois Tigeot 23125718399fSFrançois Tigeot if (version_greater(edid, 1, 2)) 23135718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); 23145718399fSFrançois Tigeot 23155718399fSFrançois Tigeot /* XXX should also look for CVT codes in VTB blocks */ 23165718399fSFrançois Tigeot 23175718399fSFrançois Tigeot return closure.modes; 23185718399fSFrançois Tigeot } 23195718399fSFrançois Tigeot 23205718399fSFrançois Tigeot static void 23215718399fSFrançois Tigeot do_detailed_mode(struct detailed_timing *timing, void *c) 23225718399fSFrançois Tigeot { 23235718399fSFrançois Tigeot struct detailed_mode_closure *closure = c; 23245718399fSFrançois Tigeot struct drm_display_mode *newmode; 23255718399fSFrançois Tigeot 23265718399fSFrançois Tigeot if (timing->pixel_clock) { 23275718399fSFrançois Tigeot newmode = drm_mode_detailed(closure->connector->dev, 23285718399fSFrançois Tigeot closure->edid, timing, 23295718399fSFrançois Tigeot closure->quirks); 23305718399fSFrançois Tigeot if (!newmode) 23315718399fSFrançois Tigeot return; 23325718399fSFrançois Tigeot 23335718399fSFrançois Tigeot if (closure->preferred) 23345718399fSFrançois Tigeot newmode->type |= DRM_MODE_TYPE_PREFERRED; 23355718399fSFrançois Tigeot 23365718399fSFrançois Tigeot drm_mode_probed_add(closure->connector, newmode); 23375718399fSFrançois Tigeot closure->modes++; 23385718399fSFrançois Tigeot closure->preferred = 0; 23395718399fSFrançois Tigeot } 23405718399fSFrançois Tigeot } 23415718399fSFrançois Tigeot 23425718399fSFrançois Tigeot /* 23435718399fSFrançois Tigeot * add_detailed_modes - Add modes from detailed timings 23445718399fSFrançois Tigeot * @connector: attached connector 23455718399fSFrançois Tigeot * @edid: EDID block to scan 23465718399fSFrançois Tigeot * @quirks: quirks to apply 23475718399fSFrançois Tigeot */ 23485718399fSFrançois Tigeot static int 23495718399fSFrançois Tigeot add_detailed_modes(struct drm_connector *connector, struct edid *edid, 23505718399fSFrançois Tigeot u32 quirks) 23515718399fSFrançois Tigeot { 23525718399fSFrançois Tigeot struct detailed_mode_closure closure = { 23535718399fSFrançois Tigeot connector, 23545718399fSFrançois Tigeot edid, 23555718399fSFrançois Tigeot 1, 23565718399fSFrançois Tigeot quirks, 23575718399fSFrançois Tigeot 0 23585718399fSFrançois Tigeot }; 23595718399fSFrançois Tigeot 23605718399fSFrançois Tigeot if (closure.preferred && !version_greater(edid, 1, 3)) 23615718399fSFrançois Tigeot closure.preferred = 23625718399fSFrançois Tigeot (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); 23635718399fSFrançois Tigeot 23645718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); 23655718399fSFrançois Tigeot 23665718399fSFrançois Tigeot return closure.modes; 23675718399fSFrançois Tigeot } 23685718399fSFrançois Tigeot 23695718399fSFrançois Tigeot #define AUDIO_BLOCK 0x01 23706e29dde8SFrançois Tigeot #define VIDEO_BLOCK 0x02 23715718399fSFrançois Tigeot #define VENDOR_BLOCK 0x03 23725718399fSFrançois Tigeot #define SPEAKER_BLOCK 0x04 2373a2fdbec6SFrançois Tigeot #define VIDEO_CAPABILITY_BLOCK 0x07 23745718399fSFrançois Tigeot #define EDID_BASIC_AUDIO (1 << 6) 23756e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB444 (1 << 5) 23766e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB422 (1 << 4) 2377a2fdbec6SFrançois Tigeot #define EDID_CEA_VCDB_QS (1 << 6) 23785718399fSFrançois Tigeot 2379*9edbd4a0SFrançois Tigeot /* 23805718399fSFrançois Tigeot * Search EDID for CEA extension block. 23815718399fSFrançois Tigeot */ 2382*9edbd4a0SFrançois Tigeot static u8 *drm_find_cea_extension(struct edid *edid) 23835718399fSFrançois Tigeot { 23845718399fSFrançois Tigeot u8 *edid_ext = NULL; 23855718399fSFrançois Tigeot int i; 23865718399fSFrançois Tigeot 23875718399fSFrançois Tigeot /* No EDID or EDID extensions */ 23885718399fSFrançois Tigeot if (edid == NULL || edid->extensions == 0) 23895718399fSFrançois Tigeot return NULL; 23905718399fSFrançois Tigeot 23915718399fSFrançois Tigeot /* Find CEA extension */ 23925718399fSFrançois Tigeot for (i = 0; i < edid->extensions; i++) { 23935718399fSFrançois Tigeot edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); 23945718399fSFrançois Tigeot if (edid_ext[0] == CEA_EXT) 23955718399fSFrançois Tigeot break; 23965718399fSFrançois Tigeot } 23975718399fSFrançois Tigeot 23985718399fSFrançois Tigeot if (i == edid->extensions) 23995718399fSFrançois Tigeot return NULL; 24005718399fSFrançois Tigeot 24015718399fSFrançois Tigeot return edid_ext; 24025718399fSFrançois Tigeot } 2403*9edbd4a0SFrançois Tigeot 2404*9edbd4a0SFrançois Tigeot /* 2405*9edbd4a0SFrançois Tigeot * Calculate the alternate clock for the CEA mode 2406*9edbd4a0SFrançois Tigeot * (60Hz vs. 59.94Hz etc.) 2407*9edbd4a0SFrançois Tigeot */ 2408*9edbd4a0SFrançois Tigeot static unsigned int 2409*9edbd4a0SFrançois Tigeot cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) 2410*9edbd4a0SFrançois Tigeot { 2411*9edbd4a0SFrançois Tigeot unsigned int clock = cea_mode->clock; 2412*9edbd4a0SFrançois Tigeot 2413*9edbd4a0SFrançois Tigeot if (cea_mode->vrefresh % 6 != 0) 2414*9edbd4a0SFrançois Tigeot return clock; 2415*9edbd4a0SFrançois Tigeot 2416*9edbd4a0SFrançois Tigeot /* 2417*9edbd4a0SFrançois Tigeot * edid_cea_modes contains the 59.94Hz 2418*9edbd4a0SFrançois Tigeot * variant for 240 and 480 line modes, 2419*9edbd4a0SFrançois Tigeot * and the 60Hz variant otherwise. 2420*9edbd4a0SFrançois Tigeot */ 2421*9edbd4a0SFrançois Tigeot if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480) 2422*9edbd4a0SFrançois Tigeot clock = clock * 1001 / 1000; 2423*9edbd4a0SFrançois Tigeot else 2424*9edbd4a0SFrançois Tigeot clock = DIV_ROUND_UP(clock * 1000, 1001); 2425*9edbd4a0SFrançois Tigeot 2426*9edbd4a0SFrançois Tigeot return clock; 2427*9edbd4a0SFrançois Tigeot } 24285718399fSFrançois Tigeot 2429d82bf20eSFrançois Tigeot /** 2430d82bf20eSFrançois Tigeot * drm_match_cea_mode - look for a CEA mode matching given mode 2431d82bf20eSFrançois Tigeot * @to_match: display mode 2432d82bf20eSFrançois Tigeot * 2433d82bf20eSFrançois Tigeot * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 2434d82bf20eSFrançois Tigeot * mode. 24356e29dde8SFrançois Tigeot */ 2436a2fdbec6SFrançois Tigeot u8 drm_match_cea_mode(const struct drm_display_mode *to_match) 24375718399fSFrançois Tigeot { 24386e29dde8SFrançois Tigeot u8 mode; 24396e29dde8SFrançois Tigeot 2440*9edbd4a0SFrançois Tigeot if (!to_match->clock) 2441*9edbd4a0SFrançois Tigeot return 0; 24426e29dde8SFrançois Tigeot 2443*9edbd4a0SFrançois Tigeot for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) { 2444*9edbd4a0SFrançois Tigeot const struct drm_display_mode *cea_mode = &edid_cea_modes[mode]; 2445*9edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 2446*9edbd4a0SFrançois Tigeot 2447*9edbd4a0SFrançois Tigeot /* Check both 60Hz and 59.94Hz */ 2448*9edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 2449*9edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 2450*9edbd4a0SFrançois Tigeot 2451*9edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 2452*9edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 2453*9edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode)) 24546e29dde8SFrançois Tigeot return mode + 1; 24556e29dde8SFrançois Tigeot } 24566e29dde8SFrançois Tigeot return 0; 24576e29dde8SFrançois Tigeot } 24586e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_match_cea_mode); 24596e29dde8SFrançois Tigeot 2460*9edbd4a0SFrançois Tigeot /* 2461*9edbd4a0SFrançois Tigeot * Calculate the alternate clock for HDMI modes (those from the HDMI vendor 2462*9edbd4a0SFrançois Tigeot * specific block). 2463*9edbd4a0SFrançois Tigeot * 2464*9edbd4a0SFrançois Tigeot * It's almost like cea_mode_alternate_clock(), we just need to add an 2465*9edbd4a0SFrançois Tigeot * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this 2466*9edbd4a0SFrançois Tigeot * one. 2467*9edbd4a0SFrançois Tigeot */ 2468*9edbd4a0SFrançois Tigeot static unsigned int 2469*9edbd4a0SFrançois Tigeot hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode) 2470*9edbd4a0SFrançois Tigeot { 2471*9edbd4a0SFrançois Tigeot if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160) 2472*9edbd4a0SFrançois Tigeot return hdmi_mode->clock; 2473*9edbd4a0SFrançois Tigeot 2474*9edbd4a0SFrançois Tigeot return cea_mode_alternate_clock(hdmi_mode); 2475*9edbd4a0SFrançois Tigeot } 2476*9edbd4a0SFrançois Tigeot 2477*9edbd4a0SFrançois Tigeot /* 2478*9edbd4a0SFrançois Tigeot * drm_match_hdmi_mode - look for a HDMI mode matching given mode 2479*9edbd4a0SFrançois Tigeot * @to_match: display mode 2480*9edbd4a0SFrançois Tigeot * 2481*9edbd4a0SFrançois Tigeot * An HDMI mode is one defined in the HDMI vendor specific block. 2482*9edbd4a0SFrançois Tigeot * 2483*9edbd4a0SFrançois Tigeot * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one. 2484*9edbd4a0SFrançois Tigeot */ 2485*9edbd4a0SFrançois Tigeot static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match) 2486*9edbd4a0SFrançois Tigeot { 2487*9edbd4a0SFrançois Tigeot u8 mode; 2488*9edbd4a0SFrançois Tigeot 2489*9edbd4a0SFrançois Tigeot if (!to_match->clock) 2490*9edbd4a0SFrançois Tigeot return 0; 2491*9edbd4a0SFrançois Tigeot 2492*9edbd4a0SFrançois Tigeot for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) { 2493*9edbd4a0SFrançois Tigeot const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode]; 2494*9edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 2495*9edbd4a0SFrançois Tigeot 2496*9edbd4a0SFrançois Tigeot /* Make sure to also match alternate clocks */ 2497*9edbd4a0SFrançois Tigeot clock1 = hdmi_mode->clock; 2498*9edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(hdmi_mode); 2499*9edbd4a0SFrançois Tigeot 2500*9edbd4a0SFrançois Tigeot if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || 2501*9edbd4a0SFrançois Tigeot KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && 2502*9edbd4a0SFrançois Tigeot drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode)) 2503*9edbd4a0SFrançois Tigeot return mode + 1; 2504*9edbd4a0SFrançois Tigeot } 2505*9edbd4a0SFrançois Tigeot return 0; 2506*9edbd4a0SFrançois Tigeot } 25076e29dde8SFrançois Tigeot 25086e29dde8SFrançois Tigeot static int 2509*9edbd4a0SFrançois Tigeot add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) 25106e29dde8SFrançois Tigeot { 25116e29dde8SFrançois Tigeot struct drm_device *dev = connector->dev; 2512*9edbd4a0SFrançois Tigeot struct drm_display_mode *mode, *tmp; 2513*9edbd4a0SFrançois Tigeot LINUX_LIST_HEAD(list); 25146e29dde8SFrançois Tigeot int modes = 0; 25156e29dde8SFrançois Tigeot 2516*9edbd4a0SFrançois Tigeot /* Don't add CEA modes if the CEA extension block is missing */ 2517*9edbd4a0SFrançois Tigeot if (!drm_find_cea_extension(edid)) 2518*9edbd4a0SFrançois Tigeot return 0; 2519*9edbd4a0SFrançois Tigeot 2520*9edbd4a0SFrançois Tigeot /* 2521*9edbd4a0SFrançois Tigeot * Go through all probed modes and create a new mode 2522*9edbd4a0SFrançois Tigeot * with the alternate clock for certain CEA modes. 2523*9edbd4a0SFrançois Tigeot */ 2524*9edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 2525*9edbd4a0SFrançois Tigeot const struct drm_display_mode *cea_mode = NULL; 25266e29dde8SFrançois Tigeot struct drm_display_mode *newmode; 2527*9edbd4a0SFrançois Tigeot u8 mode_idx = drm_match_cea_mode(mode) - 1; 2528*9edbd4a0SFrançois Tigeot unsigned int clock1, clock2; 2529*9edbd4a0SFrançois Tigeot 2530*9edbd4a0SFrançois Tigeot if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { 2531*9edbd4a0SFrançois Tigeot cea_mode = &edid_cea_modes[mode_idx]; 2532*9edbd4a0SFrançois Tigeot clock2 = cea_mode_alternate_clock(cea_mode); 2533*9edbd4a0SFrançois Tigeot } else { 2534*9edbd4a0SFrançois Tigeot mode_idx = drm_match_hdmi_mode(mode) - 1; 2535*9edbd4a0SFrançois Tigeot if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { 2536*9edbd4a0SFrançois Tigeot cea_mode = &edid_4k_modes[mode_idx]; 2537*9edbd4a0SFrançois Tigeot clock2 = hdmi_mode_alternate_clock(cea_mode); 2538*9edbd4a0SFrançois Tigeot } 2539*9edbd4a0SFrançois Tigeot } 2540*9edbd4a0SFrançois Tigeot 2541*9edbd4a0SFrançois Tigeot if (!cea_mode) 2542*9edbd4a0SFrançois Tigeot continue; 2543*9edbd4a0SFrançois Tigeot 2544*9edbd4a0SFrançois Tigeot clock1 = cea_mode->clock; 2545*9edbd4a0SFrançois Tigeot 2546*9edbd4a0SFrançois Tigeot if (clock1 == clock2) 2547*9edbd4a0SFrançois Tigeot continue; 2548*9edbd4a0SFrançois Tigeot 2549*9edbd4a0SFrançois Tigeot if (mode->clock != clock1 && mode->clock != clock2) 2550*9edbd4a0SFrançois Tigeot continue; 2551*9edbd4a0SFrançois Tigeot 2552*9edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, cea_mode); 2553*9edbd4a0SFrançois Tigeot if (!newmode) 2554*9edbd4a0SFrançois Tigeot continue; 2555*9edbd4a0SFrançois Tigeot 2556*9edbd4a0SFrançois Tigeot /* Carry over the stereo flags */ 2557*9edbd4a0SFrançois Tigeot newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK; 2558*9edbd4a0SFrançois Tigeot 2559*9edbd4a0SFrançois Tigeot /* 2560*9edbd4a0SFrançois Tigeot * The current mode could be either variant. Make 2561*9edbd4a0SFrançois Tigeot * sure to pick the "other" clock for the new mode. 2562*9edbd4a0SFrançois Tigeot */ 2563*9edbd4a0SFrançois Tigeot if (mode->clock != clock1) 2564*9edbd4a0SFrançois Tigeot newmode->clock = clock1; 2565*9edbd4a0SFrançois Tigeot else 2566*9edbd4a0SFrançois Tigeot newmode->clock = clock2; 2567*9edbd4a0SFrançois Tigeot 2568*9edbd4a0SFrançois Tigeot list_add_tail(&newmode->head, &list); 2569*9edbd4a0SFrançois Tigeot } 2570*9edbd4a0SFrançois Tigeot 2571*9edbd4a0SFrançois Tigeot list_for_each_entry_safe(mode, tmp, &list, head) { 2572*9edbd4a0SFrançois Tigeot list_del(&mode->head); 2573*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 2574*9edbd4a0SFrançois Tigeot modes++; 2575*9edbd4a0SFrançois Tigeot } 2576*9edbd4a0SFrançois Tigeot 2577*9edbd4a0SFrançois Tigeot return modes; 2578*9edbd4a0SFrançois Tigeot } 2579*9edbd4a0SFrançois Tigeot 2580*9edbd4a0SFrançois Tigeot static struct drm_display_mode * 2581*9edbd4a0SFrançois Tigeot drm_display_mode_from_vic_index(struct drm_connector *connector, 2582*9edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, 2583*9edbd4a0SFrançois Tigeot u8 video_index) 2584*9edbd4a0SFrançois Tigeot { 2585*9edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 2586*9edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 2587*9edbd4a0SFrançois Tigeot u8 cea_mode; 2588*9edbd4a0SFrançois Tigeot 2589*9edbd4a0SFrançois Tigeot if (video_db == NULL || video_index >= video_len) 2590*9edbd4a0SFrançois Tigeot return NULL; 2591*9edbd4a0SFrançois Tigeot 2592*9edbd4a0SFrançois Tigeot /* CEA modes are numbered 1..127 */ 2593*9edbd4a0SFrançois Tigeot cea_mode = (video_db[video_index] & 127) - 1; 2594*9edbd4a0SFrançois Tigeot if (cea_mode >= ARRAY_SIZE(edid_cea_modes)) 2595*9edbd4a0SFrançois Tigeot return NULL; 2596*9edbd4a0SFrançois Tigeot 2597*9edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); 2598*9edbd4a0SFrançois Tigeot newmode->vrefresh = 0; 2599*9edbd4a0SFrançois Tigeot 2600*9edbd4a0SFrançois Tigeot return newmode; 2601*9edbd4a0SFrançois Tigeot } 2602*9edbd4a0SFrançois Tigeot 2603*9edbd4a0SFrançois Tigeot static int 2604*9edbd4a0SFrançois Tigeot do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) 2605*9edbd4a0SFrançois Tigeot { 2606*9edbd4a0SFrançois Tigeot int i, modes = 0; 2607*9edbd4a0SFrançois Tigeot 2608*9edbd4a0SFrançois Tigeot for (i = 0; i < len; i++) { 2609*9edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 2610*9edbd4a0SFrançois Tigeot mode = drm_display_mode_from_vic_index(connector, db, len, i); 2611*9edbd4a0SFrançois Tigeot if (mode) { 2612*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, mode); 2613*9edbd4a0SFrançois Tigeot modes++; 2614*9edbd4a0SFrançois Tigeot } 2615*9edbd4a0SFrançois Tigeot } 2616*9edbd4a0SFrançois Tigeot 2617*9edbd4a0SFrançois Tigeot return modes; 2618*9edbd4a0SFrançois Tigeot } 2619*9edbd4a0SFrançois Tigeot 2620*9edbd4a0SFrançois Tigeot struct stereo_mandatory_mode { 2621*9edbd4a0SFrançois Tigeot int width, height, vrefresh; 2622*9edbd4a0SFrançois Tigeot unsigned int flags; 2623*9edbd4a0SFrançois Tigeot }; 2624*9edbd4a0SFrançois Tigeot 2625*9edbd4a0SFrançois Tigeot static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { 2626*9edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 2627*9edbd4a0SFrançois Tigeot { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, 2628*9edbd4a0SFrançois Tigeot { 1920, 1080, 50, 2629*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 2630*9edbd4a0SFrançois Tigeot { 1920, 1080, 60, 2631*9edbd4a0SFrançois Tigeot DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, 2632*9edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 2633*9edbd4a0SFrançois Tigeot { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, 2634*9edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, 2635*9edbd4a0SFrançois Tigeot { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } 2636*9edbd4a0SFrançois Tigeot }; 2637*9edbd4a0SFrançois Tigeot 2638*9edbd4a0SFrançois Tigeot static bool 2639*9edbd4a0SFrançois Tigeot stereo_match_mandatory(const struct drm_display_mode *mode, 2640*9edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *stereo_mode) 2641*9edbd4a0SFrançois Tigeot { 2642*9edbd4a0SFrançois Tigeot unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; 2643*9edbd4a0SFrançois Tigeot 2644*9edbd4a0SFrançois Tigeot return mode->hdisplay == stereo_mode->width && 2645*9edbd4a0SFrançois Tigeot mode->vdisplay == stereo_mode->height && 2646*9edbd4a0SFrançois Tigeot interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && 2647*9edbd4a0SFrançois Tigeot drm_mode_vrefresh(mode) == stereo_mode->vrefresh; 2648*9edbd4a0SFrançois Tigeot } 2649*9edbd4a0SFrançois Tigeot 2650*9edbd4a0SFrançois Tigeot static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) 2651*9edbd4a0SFrançois Tigeot { 2652*9edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 2653*9edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 2654*9edbd4a0SFrançois Tigeot struct list_head stereo_modes; 2655*9edbd4a0SFrançois Tigeot int modes = 0, i; 2656*9edbd4a0SFrançois Tigeot 2657*9edbd4a0SFrançois Tigeot INIT_LIST_HEAD(&stereo_modes); 2658*9edbd4a0SFrançois Tigeot 2659*9edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 2660*9edbd4a0SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { 2661*9edbd4a0SFrançois Tigeot const struct stereo_mandatory_mode *mandatory; 2662*9edbd4a0SFrançois Tigeot struct drm_display_mode *new_mode; 2663*9edbd4a0SFrançois Tigeot 2664*9edbd4a0SFrançois Tigeot if (!stereo_match_mandatory(mode, 2665*9edbd4a0SFrançois Tigeot &stereo_mandatory_modes[i])) 2666*9edbd4a0SFrançois Tigeot continue; 2667*9edbd4a0SFrançois Tigeot 2668*9edbd4a0SFrançois Tigeot mandatory = &stereo_mandatory_modes[i]; 2669*9edbd4a0SFrançois Tigeot new_mode = drm_mode_duplicate(dev, mode); 2670*9edbd4a0SFrançois Tigeot if (!new_mode) 2671*9edbd4a0SFrançois Tigeot continue; 2672*9edbd4a0SFrançois Tigeot 2673*9edbd4a0SFrançois Tigeot new_mode->flags |= mandatory->flags; 2674*9edbd4a0SFrançois Tigeot list_add_tail(&new_mode->head, &stereo_modes); 2675*9edbd4a0SFrançois Tigeot modes++; 2676*9edbd4a0SFrançois Tigeot } 2677*9edbd4a0SFrançois Tigeot } 2678*9edbd4a0SFrançois Tigeot 2679*9edbd4a0SFrançois Tigeot list_splice_tail(&stereo_modes, &connector->probed_modes); 2680*9edbd4a0SFrançois Tigeot 2681*9edbd4a0SFrançois Tigeot return modes; 2682*9edbd4a0SFrançois Tigeot } 2683*9edbd4a0SFrançois Tigeot 2684*9edbd4a0SFrançois Tigeot static int add_hdmi_mode(struct drm_connector *connector, u8 vic) 2685*9edbd4a0SFrançois Tigeot { 2686*9edbd4a0SFrançois Tigeot struct drm_device *dev = connector->dev; 2687*9edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 2688*9edbd4a0SFrançois Tigeot 2689*9edbd4a0SFrançois Tigeot vic--; /* VICs start at 1 */ 2690*9edbd4a0SFrançois Tigeot if (vic >= ARRAY_SIZE(edid_4k_modes)) { 2691*9edbd4a0SFrançois Tigeot DRM_ERROR("Unknown HDMI VIC: %d\n", vic); 2692*9edbd4a0SFrançois Tigeot return 0; 2693*9edbd4a0SFrançois Tigeot } 2694*9edbd4a0SFrançois Tigeot 2695*9edbd4a0SFrançois Tigeot newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); 2696*9edbd4a0SFrançois Tigeot if (!newmode) 2697*9edbd4a0SFrançois Tigeot return 0; 2698*9edbd4a0SFrançois Tigeot 2699*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 2700*9edbd4a0SFrançois Tigeot 2701*9edbd4a0SFrançois Tigeot return 1; 2702*9edbd4a0SFrançois Tigeot } 2703*9edbd4a0SFrançois Tigeot 2704*9edbd4a0SFrançois Tigeot static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, 2705*9edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len, u8 video_index) 2706*9edbd4a0SFrançois Tigeot { 2707*9edbd4a0SFrançois Tigeot struct drm_display_mode *newmode; 2708*9edbd4a0SFrançois Tigeot int modes = 0; 2709*9edbd4a0SFrançois Tigeot 2710*9edbd4a0SFrançois Tigeot if (structure & (1 << 0)) { 2711*9edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 2712*9edbd4a0SFrançois Tigeot video_len, 2713*9edbd4a0SFrançois Tigeot video_index); 27146e29dde8SFrançois Tigeot if (newmode) { 2715*9edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; 27166e29dde8SFrançois Tigeot drm_mode_probed_add(connector, newmode); 27176e29dde8SFrançois Tigeot modes++; 27186e29dde8SFrançois Tigeot } 27196e29dde8SFrançois Tigeot } 2720*9edbd4a0SFrançois Tigeot if (structure & (1 << 6)) { 2721*9edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 2722*9edbd4a0SFrançois Tigeot video_len, 2723*9edbd4a0SFrançois Tigeot video_index); 2724*9edbd4a0SFrançois Tigeot if (newmode) { 2725*9edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 2726*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 2727*9edbd4a0SFrançois Tigeot modes++; 2728*9edbd4a0SFrançois Tigeot } 2729*9edbd4a0SFrançois Tigeot } 2730*9edbd4a0SFrançois Tigeot if (structure & (1 << 8)) { 2731*9edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, video_db, 2732*9edbd4a0SFrançois Tigeot video_len, 2733*9edbd4a0SFrançois Tigeot video_index); 2734*9edbd4a0SFrançois Tigeot if (newmode) { 2735*9edbd4a0SFrançois Tigeot newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 2736*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 2737*9edbd4a0SFrançois Tigeot modes++; 2738*9edbd4a0SFrançois Tigeot } 27396e29dde8SFrançois Tigeot } 27406e29dde8SFrançois Tigeot 27416e29dde8SFrançois Tigeot return modes; 27426e29dde8SFrançois Tigeot } 27436e29dde8SFrançois Tigeot 2744*9edbd4a0SFrançois Tigeot /* 2745*9edbd4a0SFrançois Tigeot * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block 2746*9edbd4a0SFrançois Tigeot * @connector: connector corresponding to the HDMI sink 2747*9edbd4a0SFrançois Tigeot * @db: start of the CEA vendor specific block 2748*9edbd4a0SFrançois Tigeot * @len: length of the CEA block payload, ie. one can access up to db[len] 2749*9edbd4a0SFrançois Tigeot * 2750*9edbd4a0SFrançois Tigeot * Parses the HDMI VSDB looking for modes to add to @connector. This function 2751*9edbd4a0SFrançois Tigeot * also adds the stereo 3d modes when applicable. 2752*9edbd4a0SFrançois Tigeot */ 2753*9edbd4a0SFrançois Tigeot static int 2754*9edbd4a0SFrançois Tigeot do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, 2755*9edbd4a0SFrançois Tigeot const u8 *video_db, u8 video_len) 2756*9edbd4a0SFrançois Tigeot { 2757*9edbd4a0SFrançois Tigeot int modes = 0, offset = 0, i, multi_present = 0, multi_len; 2758*9edbd4a0SFrançois Tigeot u8 vic_len, hdmi_3d_len = 0; 2759*9edbd4a0SFrançois Tigeot u16 mask; 2760*9edbd4a0SFrançois Tigeot u16 structure_all; 2761*9edbd4a0SFrançois Tigeot 2762*9edbd4a0SFrançois Tigeot if (len < 8) 2763*9edbd4a0SFrançois Tigeot goto out; 2764*9edbd4a0SFrançois Tigeot 2765*9edbd4a0SFrançois Tigeot /* no HDMI_Video_Present */ 2766*9edbd4a0SFrançois Tigeot if (!(db[8] & (1 << 5))) 2767*9edbd4a0SFrançois Tigeot goto out; 2768*9edbd4a0SFrançois Tigeot 2769*9edbd4a0SFrançois Tigeot /* Latency_Fields_Present */ 2770*9edbd4a0SFrançois Tigeot if (db[8] & (1 << 7)) 2771*9edbd4a0SFrançois Tigeot offset += 2; 2772*9edbd4a0SFrançois Tigeot 2773*9edbd4a0SFrançois Tigeot /* I_Latency_Fields_Present */ 2774*9edbd4a0SFrançois Tigeot if (db[8] & (1 << 6)) 2775*9edbd4a0SFrançois Tigeot offset += 2; 2776*9edbd4a0SFrançois Tigeot 2777*9edbd4a0SFrançois Tigeot /* the declared length is not long enough for the 2 first bytes 2778*9edbd4a0SFrançois Tigeot * of additional video format capabilities */ 2779*9edbd4a0SFrançois Tigeot if (len < (8 + offset + 2)) 2780*9edbd4a0SFrançois Tigeot goto out; 2781*9edbd4a0SFrançois Tigeot 2782*9edbd4a0SFrançois Tigeot /* 3D_Present */ 2783*9edbd4a0SFrançois Tigeot offset++; 2784*9edbd4a0SFrançois Tigeot if (db[8 + offset] & (1 << 7)) { 2785*9edbd4a0SFrançois Tigeot modes += add_hdmi_mandatory_stereo_modes(connector); 2786*9edbd4a0SFrançois Tigeot 2787*9edbd4a0SFrançois Tigeot /* 3D_Multi_present */ 2788*9edbd4a0SFrançois Tigeot multi_present = (db[8 + offset] & 0x60) >> 5; 2789*9edbd4a0SFrançois Tigeot } 2790*9edbd4a0SFrançois Tigeot 2791*9edbd4a0SFrançois Tigeot offset++; 2792*9edbd4a0SFrançois Tigeot vic_len = db[8 + offset] >> 5; 2793*9edbd4a0SFrançois Tigeot hdmi_3d_len = db[8 + offset] & 0x1f; 2794*9edbd4a0SFrançois Tigeot 2795*9edbd4a0SFrançois Tigeot for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { 2796*9edbd4a0SFrançois Tigeot u8 vic; 2797*9edbd4a0SFrançois Tigeot 2798*9edbd4a0SFrançois Tigeot vic = db[9 + offset + i]; 2799*9edbd4a0SFrançois Tigeot modes += add_hdmi_mode(connector, vic); 2800*9edbd4a0SFrançois Tigeot } 2801*9edbd4a0SFrançois Tigeot offset += 1 + vic_len; 2802*9edbd4a0SFrançois Tigeot 2803*9edbd4a0SFrançois Tigeot if (multi_present == 1) 2804*9edbd4a0SFrançois Tigeot multi_len = 2; 2805*9edbd4a0SFrançois Tigeot else if (multi_present == 2) 2806*9edbd4a0SFrançois Tigeot multi_len = 4; 2807*9edbd4a0SFrançois Tigeot else 2808*9edbd4a0SFrançois Tigeot multi_len = 0; 2809*9edbd4a0SFrançois Tigeot 2810*9edbd4a0SFrançois Tigeot if (len < (8 + offset + hdmi_3d_len - 1)) 2811*9edbd4a0SFrançois Tigeot goto out; 2812*9edbd4a0SFrançois Tigeot 2813*9edbd4a0SFrançois Tigeot if (hdmi_3d_len < multi_len) 2814*9edbd4a0SFrançois Tigeot goto out; 2815*9edbd4a0SFrançois Tigeot 2816*9edbd4a0SFrançois Tigeot if (multi_present == 1 || multi_present == 2) { 2817*9edbd4a0SFrançois Tigeot /* 3D_Structure_ALL */ 2818*9edbd4a0SFrançois Tigeot structure_all = (db[8 + offset] << 8) | db[9 + offset]; 2819*9edbd4a0SFrançois Tigeot 2820*9edbd4a0SFrançois Tigeot /* check if 3D_MASK is present */ 2821*9edbd4a0SFrançois Tigeot if (multi_present == 2) 2822*9edbd4a0SFrançois Tigeot mask = (db[10 + offset] << 8) | db[11 + offset]; 2823*9edbd4a0SFrançois Tigeot else 2824*9edbd4a0SFrançois Tigeot mask = 0xffff; 2825*9edbd4a0SFrançois Tigeot 2826*9edbd4a0SFrançois Tigeot for (i = 0; i < 16; i++) { 2827*9edbd4a0SFrançois Tigeot if (mask & (1 << i)) 2828*9edbd4a0SFrançois Tigeot modes += add_3d_struct_modes(connector, 2829*9edbd4a0SFrançois Tigeot structure_all, 2830*9edbd4a0SFrançois Tigeot video_db, 2831*9edbd4a0SFrançois Tigeot video_len, i); 2832*9edbd4a0SFrançois Tigeot } 2833*9edbd4a0SFrançois Tigeot } 2834*9edbd4a0SFrançois Tigeot 2835*9edbd4a0SFrançois Tigeot offset += multi_len; 2836*9edbd4a0SFrançois Tigeot 2837*9edbd4a0SFrançois Tigeot for (i = 0; i < (hdmi_3d_len - multi_len); i++) { 2838*9edbd4a0SFrançois Tigeot int vic_index; 2839*9edbd4a0SFrançois Tigeot struct drm_display_mode *newmode = NULL; 2840*9edbd4a0SFrançois Tigeot unsigned int newflag = 0; 2841*9edbd4a0SFrançois Tigeot bool detail_present; 2842*9edbd4a0SFrançois Tigeot 2843*9edbd4a0SFrançois Tigeot detail_present = ((db[8 + offset + i] & 0x0f) > 7); 2844*9edbd4a0SFrançois Tigeot 2845*9edbd4a0SFrançois Tigeot if (detail_present && (i + 1 == hdmi_3d_len - multi_len)) 2846*9edbd4a0SFrançois Tigeot break; 2847*9edbd4a0SFrançois Tigeot 2848*9edbd4a0SFrançois Tigeot /* 2D_VIC_order_X */ 2849*9edbd4a0SFrançois Tigeot vic_index = db[8 + offset + i] >> 4; 2850*9edbd4a0SFrançois Tigeot 2851*9edbd4a0SFrançois Tigeot /* 3D_Structure_X */ 2852*9edbd4a0SFrançois Tigeot switch (db[8 + offset + i] & 0x0f) { 2853*9edbd4a0SFrançois Tigeot case 0: 2854*9edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_FRAME_PACKING; 2855*9edbd4a0SFrançois Tigeot break; 2856*9edbd4a0SFrançois Tigeot case 6: 2857*9edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; 2858*9edbd4a0SFrançois Tigeot break; 2859*9edbd4a0SFrançois Tigeot case 8: 2860*9edbd4a0SFrançois Tigeot /* 3D_Detail_X */ 2861*9edbd4a0SFrançois Tigeot if ((db[9 + offset + i] >> 4) == 1) 2862*9edbd4a0SFrançois Tigeot newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; 2863*9edbd4a0SFrançois Tigeot break; 2864*9edbd4a0SFrançois Tigeot } 2865*9edbd4a0SFrançois Tigeot 2866*9edbd4a0SFrançois Tigeot if (newflag != 0) { 2867*9edbd4a0SFrançois Tigeot newmode = drm_display_mode_from_vic_index(connector, 2868*9edbd4a0SFrançois Tigeot video_db, 2869*9edbd4a0SFrançois Tigeot video_len, 2870*9edbd4a0SFrançois Tigeot vic_index); 2871*9edbd4a0SFrançois Tigeot 2872*9edbd4a0SFrançois Tigeot if (newmode) { 2873*9edbd4a0SFrançois Tigeot newmode->flags |= newflag; 2874*9edbd4a0SFrançois Tigeot drm_mode_probed_add(connector, newmode); 2875*9edbd4a0SFrançois Tigeot modes++; 2876*9edbd4a0SFrançois Tigeot } 2877*9edbd4a0SFrançois Tigeot } 2878*9edbd4a0SFrançois Tigeot 2879*9edbd4a0SFrançois Tigeot if (detail_present) 2880*9edbd4a0SFrançois Tigeot i++; 2881*9edbd4a0SFrançois Tigeot } 2882*9edbd4a0SFrançois Tigeot 2883*9edbd4a0SFrançois Tigeot out: 2884*9edbd4a0SFrançois Tigeot return modes; 2885*9edbd4a0SFrançois Tigeot } 2886*9edbd4a0SFrançois Tigeot 28876e29dde8SFrançois Tigeot static int 28886e29dde8SFrançois Tigeot cea_db_payload_len(const u8 *db) 28896e29dde8SFrançois Tigeot { 28906e29dde8SFrançois Tigeot return db[0] & 0x1f; 28916e29dde8SFrançois Tigeot } 28926e29dde8SFrançois Tigeot 28936e29dde8SFrançois Tigeot static int 28946e29dde8SFrançois Tigeot cea_db_tag(const u8 *db) 28956e29dde8SFrançois Tigeot { 28966e29dde8SFrançois Tigeot return db[0] >> 5; 28976e29dde8SFrançois Tigeot } 28986e29dde8SFrançois Tigeot 28996e29dde8SFrançois Tigeot static int 29006e29dde8SFrançois Tigeot cea_revision(const u8 *cea) 29016e29dde8SFrançois Tigeot { 29026e29dde8SFrançois Tigeot return cea[1]; 29036e29dde8SFrançois Tigeot } 29046e29dde8SFrançois Tigeot 29056e29dde8SFrançois Tigeot static int 29066e29dde8SFrançois Tigeot cea_db_offsets(const u8 *cea, int *start, int *end) 29076e29dde8SFrançois Tigeot { 29086e29dde8SFrançois Tigeot /* Data block offset in CEA extension block */ 29096e29dde8SFrançois Tigeot *start = 4; 29106e29dde8SFrançois Tigeot *end = cea[2]; 29116e29dde8SFrançois Tigeot if (*end == 0) 29126e29dde8SFrançois Tigeot *end = 127; 29136e29dde8SFrançois Tigeot if (*end < 4 || *end > 127) 29146e29dde8SFrançois Tigeot return -ERANGE; 29156e29dde8SFrançois Tigeot return 0; 29166e29dde8SFrançois Tigeot } 29176e29dde8SFrançois Tigeot 2918*9edbd4a0SFrançois Tigeot static bool cea_db_is_hdmi_vsdb(const u8 *db) 2919*9edbd4a0SFrançois Tigeot { 2920*9edbd4a0SFrançois Tigeot int hdmi_id; 2921*9edbd4a0SFrançois Tigeot 2922*9edbd4a0SFrançois Tigeot if (cea_db_tag(db) != VENDOR_BLOCK) 2923*9edbd4a0SFrançois Tigeot return false; 2924*9edbd4a0SFrançois Tigeot 2925*9edbd4a0SFrançois Tigeot if (cea_db_payload_len(db) < 5) 2926*9edbd4a0SFrançois Tigeot return false; 2927*9edbd4a0SFrançois Tigeot 2928*9edbd4a0SFrançois Tigeot hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); 2929*9edbd4a0SFrançois Tigeot 2930*9edbd4a0SFrançois Tigeot return hdmi_id == HDMI_IEEE_OUI; 2931*9edbd4a0SFrançois Tigeot } 2932*9edbd4a0SFrançois Tigeot 29336e29dde8SFrançois Tigeot #define for_each_cea_db(cea, i, start, end) \ 29346e29dde8SFrançois Tigeot for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) 29356e29dde8SFrançois Tigeot 29366e29dde8SFrançois Tigeot static int 29376e29dde8SFrançois Tigeot add_cea_modes(struct drm_connector *connector, struct edid *edid) 29386e29dde8SFrançois Tigeot { 2939*9edbd4a0SFrançois Tigeot const u8 *cea = drm_find_cea_extension(edid); 2940*9edbd4a0SFrançois Tigeot const u8 *db, *hdmi = NULL, *video = NULL; 2941*9edbd4a0SFrançois Tigeot u8 dbl, hdmi_len, video_len = 0; 29426e29dde8SFrançois Tigeot int modes = 0; 29436e29dde8SFrançois Tigeot 29446e29dde8SFrançois Tigeot if (cea && cea_revision(cea) >= 3) { 29456e29dde8SFrançois Tigeot int i, start, end; 29466e29dde8SFrançois Tigeot 29476e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) 29486e29dde8SFrançois Tigeot return 0; 29496e29dde8SFrançois Tigeot 29506e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 29516e29dde8SFrançois Tigeot db = &cea[i]; 29526e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 29536e29dde8SFrançois Tigeot 2954*9edbd4a0SFrançois Tigeot if (cea_db_tag(db) == VIDEO_BLOCK) { 2955*9edbd4a0SFrançois Tigeot video = db + 1; 2956*9edbd4a0SFrançois Tigeot video_len = dbl; 2957*9edbd4a0SFrançois Tigeot modes += do_cea_modes(connector, video, dbl); 2958*9edbd4a0SFrançois Tigeot } 2959*9edbd4a0SFrançois Tigeot else if (cea_db_is_hdmi_vsdb(db)) { 2960*9edbd4a0SFrançois Tigeot hdmi = db; 2961*9edbd4a0SFrançois Tigeot hdmi_len = dbl; 29626e29dde8SFrançois Tigeot } 29636e29dde8SFrançois Tigeot } 2964*9edbd4a0SFrançois Tigeot } 2965*9edbd4a0SFrançois Tigeot 2966*9edbd4a0SFrançois Tigeot /* 2967*9edbd4a0SFrançois Tigeot * We parse the HDMI VSDB after having added the cea modes as we will 2968*9edbd4a0SFrançois Tigeot * be patching their flags when the sink supports stereo 3D. 2969*9edbd4a0SFrançois Tigeot */ 2970*9edbd4a0SFrançois Tigeot if (hdmi) 2971*9edbd4a0SFrançois Tigeot modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, 2972*9edbd4a0SFrançois Tigeot video_len); 29736e29dde8SFrançois Tigeot 29746e29dde8SFrançois Tigeot return modes; 29756e29dde8SFrançois Tigeot } 29766e29dde8SFrançois Tigeot 29776e29dde8SFrançois Tigeot static void 29786e29dde8SFrançois Tigeot parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) 29796e29dde8SFrançois Tigeot { 29806e29dde8SFrançois Tigeot u8 len = cea_db_payload_len(db); 29816e29dde8SFrançois Tigeot 29826e29dde8SFrançois Tigeot if (len >= 6) { 29835718399fSFrançois Tigeot connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ 29845718399fSFrançois Tigeot connector->dvi_dual = db[6] & 1; 29856e29dde8SFrançois Tigeot } 29866e29dde8SFrançois Tigeot if (len >= 7) 29875718399fSFrançois Tigeot connector->max_tmds_clock = db[7] * 5; 29886e29dde8SFrançois Tigeot if (len >= 8) { 29895718399fSFrançois Tigeot connector->latency_present[0] = db[8] >> 7; 29905718399fSFrançois Tigeot connector->latency_present[1] = (db[8] >> 6) & 1; 29916e29dde8SFrançois Tigeot } 29926e29dde8SFrançois Tigeot if (len >= 9) 29935718399fSFrançois Tigeot connector->video_latency[0] = db[9]; 29946e29dde8SFrançois Tigeot if (len >= 10) 29955718399fSFrançois Tigeot connector->audio_latency[0] = db[10]; 29966e29dde8SFrançois Tigeot if (len >= 11) 29975718399fSFrançois Tigeot connector->video_latency[1] = db[11]; 29986e29dde8SFrançois Tigeot if (len >= 12) 29995718399fSFrançois Tigeot connector->audio_latency[1] = db[12]; 30005718399fSFrançois Tigeot 30015718399fSFrançois Tigeot DRM_DEBUG_KMS("HDMI: DVI dual %d, " 30025718399fSFrançois Tigeot "max TMDS clock %d, " 30035718399fSFrançois Tigeot "latency present %d %d, " 30045718399fSFrançois Tigeot "video latency %d %d, " 30055718399fSFrançois Tigeot "audio latency %d %d\n", 30065718399fSFrançois Tigeot connector->dvi_dual, 30075718399fSFrançois Tigeot connector->max_tmds_clock, 30085718399fSFrançois Tigeot (int) connector->latency_present[0], 30095718399fSFrançois Tigeot (int) connector->latency_present[1], 30105718399fSFrançois Tigeot connector->video_latency[0], 30115718399fSFrançois Tigeot connector->video_latency[1], 30125718399fSFrançois Tigeot connector->audio_latency[0], 30135718399fSFrançois Tigeot connector->audio_latency[1]); 30145718399fSFrançois Tigeot } 30155718399fSFrançois Tigeot 30165718399fSFrançois Tigeot static void 30175718399fSFrançois Tigeot monitor_name(struct detailed_timing *t, void *data) 30185718399fSFrançois Tigeot { 30195718399fSFrançois Tigeot if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME) 30205718399fSFrançois Tigeot *(u8 **)data = t->data.other_data.data.str.str; 30215718399fSFrançois Tigeot } 30225718399fSFrançois Tigeot 30235718399fSFrançois Tigeot /** 30245718399fSFrançois Tigeot * drm_edid_to_eld - build ELD from EDID 30255718399fSFrançois Tigeot * @connector: connector corresponding to the HDMI/DP sink 30265718399fSFrançois Tigeot * @edid: EDID to parse 30275718399fSFrançois Tigeot * 30285718399fSFrançois Tigeot * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. 30295718399fSFrançois Tigeot * Some ELD fields are left to the graphics driver caller: 30305718399fSFrançois Tigeot * - Conn_Type 30315718399fSFrançois Tigeot * - HDCP 30325718399fSFrançois Tigeot * - Port_ID 30335718399fSFrançois Tigeot */ 30345718399fSFrançois Tigeot void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) 30355718399fSFrançois Tigeot { 30365718399fSFrançois Tigeot uint8_t *eld = connector->eld; 30375718399fSFrançois Tigeot u8 *cea; 30385718399fSFrançois Tigeot u8 *name; 30395718399fSFrançois Tigeot u8 *db; 30405718399fSFrançois Tigeot int sad_count = 0; 30415718399fSFrançois Tigeot int mnl; 30425718399fSFrançois Tigeot int dbl; 30435718399fSFrançois Tigeot 30445718399fSFrançois Tigeot memset(eld, 0, sizeof(connector->eld)); 30455718399fSFrançois Tigeot 30465718399fSFrançois Tigeot cea = drm_find_cea_extension(edid); 30475718399fSFrançois Tigeot if (!cea) { 30485718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD: no CEA Extension found\n"); 30495718399fSFrançois Tigeot return; 30505718399fSFrançois Tigeot } 30515718399fSFrançois Tigeot 30525718399fSFrançois Tigeot name = NULL; 30535718399fSFrançois Tigeot drm_for_each_detailed_block((u8 *)edid, monitor_name, &name); 30545718399fSFrançois Tigeot for (mnl = 0; name && mnl < 13; mnl++) { 30555718399fSFrançois Tigeot if (name[mnl] == 0x0a) 30565718399fSFrançois Tigeot break; 30575718399fSFrançois Tigeot eld[20 + mnl] = name[mnl]; 30585718399fSFrançois Tigeot } 30595718399fSFrançois Tigeot eld[4] = (cea[1] << 5) | mnl; 30605718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20); 30615718399fSFrançois Tigeot 30625718399fSFrançois Tigeot eld[0] = 2 << 3; /* ELD version: 2 */ 30635718399fSFrançois Tigeot 30645718399fSFrançois Tigeot eld[16] = edid->mfg_id[0]; 30655718399fSFrançois Tigeot eld[17] = edid->mfg_id[1]; 30665718399fSFrançois Tigeot eld[18] = edid->prod_code[0]; 30675718399fSFrançois Tigeot eld[19] = edid->prod_code[1]; 30685718399fSFrançois Tigeot 30696e29dde8SFrançois Tigeot if (cea_revision(cea) >= 3) { 30706e29dde8SFrançois Tigeot int i, start, end; 30715718399fSFrançois Tigeot 30726e29dde8SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 30736e29dde8SFrançois Tigeot start = 0; 30746e29dde8SFrançois Tigeot end = 0; 30756e29dde8SFrançois Tigeot } 30766e29dde8SFrançois Tigeot 30776e29dde8SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 30786e29dde8SFrançois Tigeot db = &cea[i]; 30796e29dde8SFrançois Tigeot dbl = cea_db_payload_len(db); 30806e29dde8SFrançois Tigeot 30816e29dde8SFrançois Tigeot switch (cea_db_tag(db)) { 30826e29dde8SFrançois Tigeot case AUDIO_BLOCK: 30836e29dde8SFrançois Tigeot /* Audio Data Block, contains SADs */ 30845718399fSFrançois Tigeot sad_count = dbl / 3; 30856e29dde8SFrançois Tigeot if (dbl >= 1) 30865718399fSFrançois Tigeot memcpy(eld + 20 + mnl, &db[1], dbl); 30875718399fSFrançois Tigeot break; 30886e29dde8SFrançois Tigeot case SPEAKER_BLOCK: 30896e29dde8SFrançois Tigeot /* Speaker Allocation Data Block */ 30906e29dde8SFrançois Tigeot if (dbl >= 1) 30915718399fSFrançois Tigeot eld[7] = db[1]; 30925718399fSFrançois Tigeot break; 30935718399fSFrançois Tigeot case VENDOR_BLOCK: 30945718399fSFrançois Tigeot /* HDMI Vendor-Specific Data Block */ 30956e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(db)) 30965718399fSFrançois Tigeot parse_hdmi_vsdb(connector, db); 30975718399fSFrançois Tigeot break; 30985718399fSFrançois Tigeot default: 30995718399fSFrançois Tigeot break; 31005718399fSFrançois Tigeot } 31015718399fSFrançois Tigeot } 31026e29dde8SFrançois Tigeot } 31035718399fSFrançois Tigeot eld[5] |= sad_count << 4; 31045718399fSFrançois Tigeot eld[2] = (20 + mnl + sad_count * 3 + 3) / 4; 31055718399fSFrançois Tigeot 31065718399fSFrançois Tigeot DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count); 31075718399fSFrançois Tigeot } 31086e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_eld); 31095718399fSFrançois Tigeot 31105718399fSFrançois Tigeot /** 3111*9edbd4a0SFrançois Tigeot * drm_edid_to_sad - extracts SADs from EDID 3112*9edbd4a0SFrançois Tigeot * @edid: EDID to parse 3113*9edbd4a0SFrançois Tigeot * @sads: pointer that will be set to the extracted SADs 3114*9edbd4a0SFrançois Tigeot * 3115*9edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. 3116*9edbd4a0SFrançois Tigeot * Note: returned pointer needs to be kfreed 3117*9edbd4a0SFrançois Tigeot * 3118*9edbd4a0SFrançois Tigeot * Return number of found SADs or negative number on error. 3119*9edbd4a0SFrançois Tigeot */ 3120*9edbd4a0SFrançois Tigeot int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) 3121*9edbd4a0SFrançois Tigeot { 3122*9edbd4a0SFrançois Tigeot int count = 0; 3123*9edbd4a0SFrançois Tigeot int i, start, end, dbl; 3124*9edbd4a0SFrançois Tigeot u8 *cea; 3125*9edbd4a0SFrançois Tigeot 3126*9edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 3127*9edbd4a0SFrançois Tigeot if (!cea) { 3128*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 3129*9edbd4a0SFrançois Tigeot return -ENOENT; 3130*9edbd4a0SFrançois Tigeot } 3131*9edbd4a0SFrançois Tigeot 3132*9edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 3133*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 3134*9edbd4a0SFrançois Tigeot return -ENOTSUPP; 3135*9edbd4a0SFrançois Tigeot } 3136*9edbd4a0SFrançois Tigeot 3137*9edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 3138*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 3139*9edbd4a0SFrançois Tigeot return -EPROTO; 3140*9edbd4a0SFrançois Tigeot } 3141*9edbd4a0SFrançois Tigeot 3142*9edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 3143*9edbd4a0SFrançois Tigeot u8 *db = &cea[i]; 3144*9edbd4a0SFrançois Tigeot 3145*9edbd4a0SFrançois Tigeot if (cea_db_tag(db) == AUDIO_BLOCK) { 3146*9edbd4a0SFrançois Tigeot int j; 3147*9edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 3148*9edbd4a0SFrançois Tigeot 3149*9edbd4a0SFrançois Tigeot count = dbl / 3; /* SAD is 3B */ 3150*9edbd4a0SFrançois Tigeot *sads = kmalloc(count * sizeof(**sads), M_DRM, M_WAITOK); 3151*9edbd4a0SFrançois Tigeot if (!*sads) 3152*9edbd4a0SFrançois Tigeot return -ENOMEM; 3153*9edbd4a0SFrançois Tigeot for (j = 0; j < count; j++) { 3154*9edbd4a0SFrançois Tigeot u8 *sad = &db[1 + j * 3]; 3155*9edbd4a0SFrançois Tigeot 3156*9edbd4a0SFrançois Tigeot (*sads)[j].format = (sad[0] & 0x78) >> 3; 3157*9edbd4a0SFrançois Tigeot (*sads)[j].channels = sad[0] & 0x7; 3158*9edbd4a0SFrançois Tigeot (*sads)[j].freq = sad[1] & 0x7F; 3159*9edbd4a0SFrançois Tigeot (*sads)[j].byte2 = sad[2]; 3160*9edbd4a0SFrançois Tigeot } 3161*9edbd4a0SFrançois Tigeot break; 3162*9edbd4a0SFrançois Tigeot } 3163*9edbd4a0SFrançois Tigeot } 3164*9edbd4a0SFrançois Tigeot 3165*9edbd4a0SFrançois Tigeot return count; 3166*9edbd4a0SFrançois Tigeot } 3167*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_sad); 3168*9edbd4a0SFrançois Tigeot 3169*9edbd4a0SFrançois Tigeot /** 3170*9edbd4a0SFrançois Tigeot * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID 3171*9edbd4a0SFrançois Tigeot * @edid: EDID to parse 3172*9edbd4a0SFrançois Tigeot * @sadb: pointer to the speaker block 3173*9edbd4a0SFrançois Tigeot * 3174*9edbd4a0SFrançois Tigeot * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. 3175*9edbd4a0SFrançois Tigeot * Note: returned pointer needs to be kfreed 3176*9edbd4a0SFrançois Tigeot * 3177*9edbd4a0SFrançois Tigeot * Return number of found Speaker Allocation Blocks or negative number on error. 3178*9edbd4a0SFrançois Tigeot */ 3179*9edbd4a0SFrançois Tigeot int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) 3180*9edbd4a0SFrançois Tigeot { 3181*9edbd4a0SFrançois Tigeot int count = 0; 3182*9edbd4a0SFrançois Tigeot int i, start, end, dbl; 3183*9edbd4a0SFrançois Tigeot const u8 *cea; 3184*9edbd4a0SFrançois Tigeot 3185*9edbd4a0SFrançois Tigeot cea = drm_find_cea_extension(edid); 3186*9edbd4a0SFrançois Tigeot if (!cea) { 3187*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 3188*9edbd4a0SFrançois Tigeot return -ENOENT; 3189*9edbd4a0SFrançois Tigeot } 3190*9edbd4a0SFrançois Tigeot 3191*9edbd4a0SFrançois Tigeot if (cea_revision(cea) < 3) { 3192*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 3193*9edbd4a0SFrançois Tigeot return -ENOTSUPP; 3194*9edbd4a0SFrançois Tigeot } 3195*9edbd4a0SFrançois Tigeot 3196*9edbd4a0SFrançois Tigeot if (cea_db_offsets(cea, &start, &end)) { 3197*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 3198*9edbd4a0SFrançois Tigeot return -EPROTO; 3199*9edbd4a0SFrançois Tigeot } 3200*9edbd4a0SFrançois Tigeot 3201*9edbd4a0SFrançois Tigeot for_each_cea_db(cea, i, start, end) { 3202*9edbd4a0SFrançois Tigeot const u8 *db = &cea[i]; 3203*9edbd4a0SFrançois Tigeot 3204*9edbd4a0SFrançois Tigeot if (cea_db_tag(db) == SPEAKER_BLOCK) { 3205*9edbd4a0SFrançois Tigeot dbl = cea_db_payload_len(db); 3206*9edbd4a0SFrançois Tigeot 3207*9edbd4a0SFrançois Tigeot /* Speaker Allocation Data Block */ 3208*9edbd4a0SFrançois Tigeot if (dbl == 3) { 3209*9edbd4a0SFrançois Tigeot *sadb = kmalloc(dbl, M_DRM, M_WAITOK); 3210*9edbd4a0SFrançois Tigeot memcpy(*sadb, &db[1], dbl); 3211*9edbd4a0SFrançois Tigeot count = dbl; 3212*9edbd4a0SFrançois Tigeot break; 3213*9edbd4a0SFrançois Tigeot } 3214*9edbd4a0SFrançois Tigeot } 3215*9edbd4a0SFrançois Tigeot } 3216*9edbd4a0SFrançois Tigeot 3217*9edbd4a0SFrançois Tigeot return count; 3218*9edbd4a0SFrançois Tigeot } 3219*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_speaker_allocation); 3220*9edbd4a0SFrançois Tigeot 3221*9edbd4a0SFrançois Tigeot /** 32225718399fSFrançois Tigeot * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond 32235718399fSFrançois Tigeot * @connector: connector associated with the HDMI/DP sink 32245718399fSFrançois Tigeot * @mode: the display mode 32255718399fSFrançois Tigeot */ 32265718399fSFrançois Tigeot int drm_av_sync_delay(struct drm_connector *connector, 32275718399fSFrançois Tigeot struct drm_display_mode *mode) 32285718399fSFrançois Tigeot { 32295718399fSFrançois Tigeot int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 32305718399fSFrançois Tigeot int a, v; 32315718399fSFrançois Tigeot 32325718399fSFrançois Tigeot if (!connector->latency_present[0]) 32335718399fSFrançois Tigeot return 0; 32345718399fSFrançois Tigeot if (!connector->latency_present[1]) 32355718399fSFrançois Tigeot i = 0; 32365718399fSFrançois Tigeot 32375718399fSFrançois Tigeot a = connector->audio_latency[i]; 32385718399fSFrançois Tigeot v = connector->video_latency[i]; 32395718399fSFrançois Tigeot 32405718399fSFrançois Tigeot /* 32415718399fSFrançois Tigeot * HDMI/DP sink doesn't support audio or video? 32425718399fSFrançois Tigeot */ 32435718399fSFrançois Tigeot if (a == 255 || v == 255) 32445718399fSFrançois Tigeot return 0; 32455718399fSFrançois Tigeot 32465718399fSFrançois Tigeot /* 32475718399fSFrançois Tigeot * Convert raw EDID values to millisecond. 32485718399fSFrançois Tigeot * Treat unknown latency as 0ms. 32495718399fSFrançois Tigeot */ 32505718399fSFrançois Tigeot if (a) 32515718399fSFrançois Tigeot a = min(2 * (a - 1), 500); 32525718399fSFrançois Tigeot if (v) 32535718399fSFrançois Tigeot v = min(2 * (v - 1), 500); 32545718399fSFrançois Tigeot 32555718399fSFrançois Tigeot return max(v - a, 0); 32565718399fSFrançois Tigeot } 32576e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_av_sync_delay); 32585718399fSFrançois Tigeot 32595718399fSFrançois Tigeot /** 32605718399fSFrançois Tigeot * drm_select_eld - select one ELD from multiple HDMI/DP sinks 32615718399fSFrançois Tigeot * @encoder: the encoder just changed display mode 32625718399fSFrançois Tigeot * @mode: the adjusted display mode 32635718399fSFrançois Tigeot * 32645718399fSFrançois Tigeot * It's possible for one encoder to be associated with multiple HDMI/DP sinks. 32655718399fSFrançois Tigeot * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. 32665718399fSFrançois Tigeot */ 32675718399fSFrançois Tigeot struct drm_connector *drm_select_eld(struct drm_encoder *encoder, 32685718399fSFrançois Tigeot struct drm_display_mode *mode) 32695718399fSFrançois Tigeot { 32705718399fSFrançois Tigeot struct drm_connector *connector; 32715718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 32725718399fSFrançois Tigeot 32735718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 32745718399fSFrançois Tigeot if (connector->encoder == encoder && connector->eld[0]) 32755718399fSFrançois Tigeot return connector; 32765718399fSFrançois Tigeot 32775718399fSFrançois Tigeot return NULL; 32785718399fSFrançois Tigeot } 32796e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_select_eld); 32805718399fSFrançois Tigeot 32815718399fSFrançois Tigeot /** 32825718399fSFrançois Tigeot * drm_detect_hdmi_monitor - detect whether monitor is hdmi. 32835718399fSFrançois Tigeot * @edid: monitor EDID information 32845718399fSFrançois Tigeot * 32855718399fSFrançois Tigeot * Parse the CEA extension according to CEA-861-B. 32865718399fSFrançois Tigeot * Return true if HDMI, false if not or unknown. 32875718399fSFrançois Tigeot */ 32885718399fSFrançois Tigeot bool drm_detect_hdmi_monitor(struct edid *edid) 32895718399fSFrançois Tigeot { 32905718399fSFrançois Tigeot u8 *edid_ext; 32916e29dde8SFrançois Tigeot int i; 32925718399fSFrançois Tigeot int start_offset, end_offset; 32935718399fSFrançois Tigeot 32945718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 32955718399fSFrançois Tigeot if (!edid_ext) 32966e29dde8SFrançois Tigeot return false; 32975718399fSFrançois Tigeot 32986e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 32996e29dde8SFrançois Tigeot return false; 33005718399fSFrançois Tigeot 33015718399fSFrançois Tigeot /* 33025718399fSFrançois Tigeot * Because HDMI identifier is in Vendor Specific Block, 33035718399fSFrançois Tigeot * search it from all data blocks of CEA extension. 33045718399fSFrançois Tigeot */ 33056e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 33066e29dde8SFrançois Tigeot if (cea_db_is_hdmi_vsdb(&edid_ext[i])) 33076e29dde8SFrançois Tigeot return true; 33085718399fSFrançois Tigeot } 33095718399fSFrançois Tigeot 33106e29dde8SFrançois Tigeot return false; 33115718399fSFrançois Tigeot } 33126e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_hdmi_monitor); 33135718399fSFrançois Tigeot 33145718399fSFrançois Tigeot /** 33155718399fSFrançois Tigeot * drm_detect_monitor_audio - check monitor audio capability 33165718399fSFrançois Tigeot * 33175718399fSFrançois Tigeot * Monitor should have CEA extension block. 33185718399fSFrançois Tigeot * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic 33195718399fSFrançois Tigeot * audio' only. If there is any audio extension block and supported 33205718399fSFrançois Tigeot * audio format, assume at least 'basic audio' support, even if 'basic 33215718399fSFrançois Tigeot * audio' is not defined in EDID. 33225718399fSFrançois Tigeot * 33235718399fSFrançois Tigeot */ 33245718399fSFrançois Tigeot bool drm_detect_monitor_audio(struct edid *edid) 33255718399fSFrançois Tigeot { 33265718399fSFrançois Tigeot u8 *edid_ext; 33275718399fSFrançois Tigeot int i, j; 33285718399fSFrançois Tigeot bool has_audio = false; 33295718399fSFrançois Tigeot int start_offset, end_offset; 33305718399fSFrançois Tigeot 33315718399fSFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 33325718399fSFrançois Tigeot if (!edid_ext) 33335718399fSFrançois Tigeot goto end; 33345718399fSFrançois Tigeot 33355718399fSFrançois Tigeot has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0); 33365718399fSFrançois Tigeot 33375718399fSFrançois Tigeot if (has_audio) { 33385718399fSFrançois Tigeot DRM_DEBUG_KMS("Monitor has basic audio support\n"); 33395718399fSFrançois Tigeot goto end; 33405718399fSFrançois Tigeot } 33415718399fSFrançois Tigeot 33426e29dde8SFrançois Tigeot if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) 33436e29dde8SFrançois Tigeot goto end; 33445718399fSFrançois Tigeot 33456e29dde8SFrançois Tigeot for_each_cea_db(edid_ext, i, start_offset, end_offset) { 33466e29dde8SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { 33475718399fSFrançois Tigeot has_audio = true; 33486e29dde8SFrançois Tigeot for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) 33495718399fSFrançois Tigeot DRM_DEBUG_KMS("CEA audio format %d\n", 33505718399fSFrançois Tigeot (edid_ext[i + j] >> 3) & 0xf); 33515718399fSFrançois Tigeot goto end; 33525718399fSFrançois Tigeot } 33535718399fSFrançois Tigeot } 33545718399fSFrançois Tigeot end: 33555718399fSFrançois Tigeot return has_audio; 33565718399fSFrançois Tigeot } 33576e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_monitor_audio); 33585718399fSFrançois Tigeot 33595718399fSFrançois Tigeot /** 3360a2fdbec6SFrançois Tigeot * drm_rgb_quant_range_selectable - is RGB quantization range selectable? 3361a2fdbec6SFrançois Tigeot * 3362a2fdbec6SFrançois Tigeot * Check whether the monitor reports the RGB quantization range selection 3363a2fdbec6SFrançois Tigeot * as supported. The AVI infoframe can then be used to inform the monitor 3364a2fdbec6SFrançois Tigeot * which quantization range (full or limited) is used. 3365a2fdbec6SFrançois Tigeot */ 3366a2fdbec6SFrançois Tigeot bool drm_rgb_quant_range_selectable(struct edid *edid) 3367a2fdbec6SFrançois Tigeot { 3368a2fdbec6SFrançois Tigeot u8 *edid_ext; 3369a2fdbec6SFrançois Tigeot int i, start, end; 3370a2fdbec6SFrançois Tigeot 3371a2fdbec6SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 3372a2fdbec6SFrançois Tigeot if (!edid_ext) 3373a2fdbec6SFrançois Tigeot return false; 3374a2fdbec6SFrançois Tigeot 3375a2fdbec6SFrançois Tigeot if (cea_db_offsets(edid_ext, &start, &end)) 3376a2fdbec6SFrançois Tigeot return false; 3377a2fdbec6SFrançois Tigeot 3378a2fdbec6SFrançois Tigeot for_each_cea_db(edid_ext, i, start, end) { 3379a2fdbec6SFrançois Tigeot if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && 3380a2fdbec6SFrançois Tigeot cea_db_payload_len(&edid_ext[i]) == 2) { 3381a2fdbec6SFrançois Tigeot DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); 3382a2fdbec6SFrançois Tigeot return edid_ext[i + 2] & EDID_CEA_VCDB_QS; 3383a2fdbec6SFrançois Tigeot } 3384a2fdbec6SFrançois Tigeot } 3385a2fdbec6SFrançois Tigeot 3386a2fdbec6SFrançois Tigeot return false; 3387a2fdbec6SFrançois Tigeot } 3388a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_rgb_quant_range_selectable); 3389a2fdbec6SFrançois Tigeot 3390a2fdbec6SFrançois Tigeot /** 33915718399fSFrançois Tigeot * drm_add_display_info - pull display info out if present 33925718399fSFrançois Tigeot * @edid: EDID data 33935718399fSFrançois Tigeot * @info: display info (attached to connector) 33945718399fSFrançois Tigeot * 33955718399fSFrançois Tigeot * Grab any available display info and stuff it into the drm_display_info 33965718399fSFrançois Tigeot * structure that's part of the connector. Useful for tracking bpp and 33975718399fSFrançois Tigeot * color spaces. 33985718399fSFrançois Tigeot */ 33995718399fSFrançois Tigeot static void drm_add_display_info(struct edid *edid, 34005718399fSFrançois Tigeot struct drm_display_info *info) 34015718399fSFrançois Tigeot { 34025718399fSFrançois Tigeot u8 *edid_ext; 34035718399fSFrançois Tigeot 34045718399fSFrançois Tigeot info->width_mm = edid->width_cm * 10; 34055718399fSFrançois Tigeot info->height_mm = edid->height_cm * 10; 34065718399fSFrançois Tigeot 34075718399fSFrançois Tigeot /* driver figures it out in this case */ 34085718399fSFrançois Tigeot info->bpc = 0; 34095718399fSFrançois Tigeot info->color_formats = 0; 34105718399fSFrançois Tigeot 34116e29dde8SFrançois Tigeot if (edid->revision < 3) 34125718399fSFrançois Tigeot return; 34135718399fSFrançois Tigeot 34145718399fSFrançois Tigeot if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) 34155718399fSFrançois Tigeot return; 34165718399fSFrançois Tigeot 34176e29dde8SFrançois Tigeot /* Get data from CEA blocks if present */ 34186e29dde8SFrançois Tigeot edid_ext = drm_find_cea_extension(edid); 34196e29dde8SFrançois Tigeot if (edid_ext) { 34206e29dde8SFrançois Tigeot info->cea_rev = edid_ext[1]; 34216e29dde8SFrançois Tigeot 34226e29dde8SFrançois Tigeot /* The existence of a CEA block should imply RGB support */ 34236e29dde8SFrançois Tigeot info->color_formats = DRM_COLOR_FORMAT_RGB444; 34246e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB444) 34256e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 34266e29dde8SFrançois Tigeot if (edid_ext[3] & EDID_CEA_YCRCB422) 34276e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 34286e29dde8SFrançois Tigeot } 34296e29dde8SFrançois Tigeot 34306e29dde8SFrançois Tigeot /* Only defined for 1.4 with digital displays */ 34316e29dde8SFrançois Tigeot if (edid->revision < 4) 34326e29dde8SFrançois Tigeot return; 34336e29dde8SFrançois Tigeot 34345718399fSFrançois Tigeot switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { 34355718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_6: 34365718399fSFrançois Tigeot info->bpc = 6; 34375718399fSFrançois Tigeot break; 34385718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_8: 34395718399fSFrançois Tigeot info->bpc = 8; 34405718399fSFrançois Tigeot break; 34415718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_10: 34425718399fSFrançois Tigeot info->bpc = 10; 34435718399fSFrançois Tigeot break; 34445718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_12: 34455718399fSFrançois Tigeot info->bpc = 12; 34465718399fSFrançois Tigeot break; 34475718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_14: 34485718399fSFrançois Tigeot info->bpc = 14; 34495718399fSFrançois Tigeot break; 34505718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_16: 34515718399fSFrançois Tigeot info->bpc = 16; 34525718399fSFrançois Tigeot break; 34535718399fSFrançois Tigeot case DRM_EDID_DIGITAL_DEPTH_UNDEF: 34545718399fSFrançois Tigeot default: 34555718399fSFrançois Tigeot info->bpc = 0; 34565718399fSFrançois Tigeot break; 34575718399fSFrançois Tigeot } 34585718399fSFrançois Tigeot 34596e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_RGB444; 34606e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444) 34616e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; 34626e29dde8SFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) 34636e29dde8SFrançois Tigeot info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; 34645718399fSFrançois Tigeot } 34655718399fSFrançois Tigeot 34665718399fSFrançois Tigeot /** 34675718399fSFrançois Tigeot * drm_add_edid_modes - add modes from EDID data, if available 34685718399fSFrançois Tigeot * @connector: connector we're probing 34695718399fSFrançois Tigeot * @edid: edid data 34705718399fSFrançois Tigeot * 34715718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. 34725718399fSFrançois Tigeot * 34735718399fSFrançois Tigeot * Return number of modes added or 0 if we couldn't find any. 34745718399fSFrançois Tigeot */ 34755718399fSFrançois Tigeot int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) 34765718399fSFrançois Tigeot { 34775718399fSFrançois Tigeot int num_modes = 0; 34785718399fSFrançois Tigeot u32 quirks; 34795718399fSFrançois Tigeot 34805718399fSFrançois Tigeot if (edid == NULL) { 34815718399fSFrançois Tigeot return 0; 34825718399fSFrançois Tigeot } 34835718399fSFrançois Tigeot if (!drm_edid_is_valid(edid)) { 34846e29dde8SFrançois Tigeot dev_warn(connector->dev->dev, "%s: EDID invalid.\n", 34855718399fSFrançois Tigeot drm_get_connector_name(connector)); 34865718399fSFrançois Tigeot return 0; 34875718399fSFrançois Tigeot } 34885718399fSFrançois Tigeot 34895718399fSFrançois Tigeot quirks = edid_get_quirks(edid); 34905718399fSFrançois Tigeot 34915718399fSFrançois Tigeot /* 34925718399fSFrançois Tigeot * EDID spec says modes should be preferred in this order: 34935718399fSFrançois Tigeot * - preferred detailed mode 34945718399fSFrançois Tigeot * - other detailed modes from base block 34955718399fSFrançois Tigeot * - detailed modes from extension blocks 34965718399fSFrançois Tigeot * - CVT 3-byte code modes 34975718399fSFrançois Tigeot * - standard timing codes 34985718399fSFrançois Tigeot * - established timing codes 34995718399fSFrançois Tigeot * - modes inferred from GTF or CVT range information 35005718399fSFrançois Tigeot * 35015718399fSFrançois Tigeot * We get this pretty much right. 35025718399fSFrançois Tigeot * 35035718399fSFrançois Tigeot * XXX order for additional mode types in extension blocks? 35045718399fSFrançois Tigeot */ 35055718399fSFrançois Tigeot num_modes += add_detailed_modes(connector, edid, quirks); 35065718399fSFrançois Tigeot num_modes += add_cvt_modes(connector, edid); 35075718399fSFrançois Tigeot num_modes += add_standard_modes(connector, edid); 35085718399fSFrançois Tigeot num_modes += add_established_modes(connector, edid); 3509d82bf20eSFrançois Tigeot if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) 35105718399fSFrançois Tigeot num_modes += add_inferred_modes(connector, edid); 35116e29dde8SFrançois Tigeot num_modes += add_cea_modes(connector, edid); 3512*9edbd4a0SFrançois Tigeot num_modes += add_alternate_cea_modes(connector, edid); 35135718399fSFrançois Tigeot 35145718399fSFrançois Tigeot if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) 35155718399fSFrançois Tigeot edid_fixup_preferred(connector, quirks); 35165718399fSFrançois Tigeot 35175718399fSFrançois Tigeot drm_add_display_info(edid, &connector->display_info); 35185718399fSFrançois Tigeot 3519*9edbd4a0SFrançois Tigeot if (quirks & EDID_QUIRK_FORCE_8BPC) 3520*9edbd4a0SFrançois Tigeot connector->display_info.bpc = 8; 3521*9edbd4a0SFrançois Tigeot 35225718399fSFrançois Tigeot return num_modes; 35235718399fSFrançois Tigeot } 35246e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_edid_modes); 35255718399fSFrançois Tigeot 35265718399fSFrançois Tigeot /** 35275718399fSFrançois Tigeot * drm_add_modes_noedid - add modes for the connectors without EDID 35285718399fSFrançois Tigeot * @connector: connector we're probing 35295718399fSFrançois Tigeot * @hdisplay: the horizontal display limit 35305718399fSFrançois Tigeot * @vdisplay: the vertical display limit 35315718399fSFrançois Tigeot * 35325718399fSFrançois Tigeot * Add the specified modes to the connector's mode list. Only when the 35335718399fSFrançois Tigeot * hdisplay/vdisplay is not beyond the given limit, it will be added. 35345718399fSFrançois Tigeot * 35355718399fSFrançois Tigeot * Return number of modes added or 0 if we couldn't find any. 35365718399fSFrançois Tigeot */ 35375718399fSFrançois Tigeot int drm_add_modes_noedid(struct drm_connector *connector, 35385718399fSFrançois Tigeot int hdisplay, int vdisplay) 35395718399fSFrançois Tigeot { 35405718399fSFrançois Tigeot int i, count, num_modes = 0; 35415718399fSFrançois Tigeot struct drm_display_mode *mode; 35425718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 35435718399fSFrançois Tigeot 35445718399fSFrançois Tigeot count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); 35455718399fSFrançois Tigeot if (hdisplay < 0) 35465718399fSFrançois Tigeot hdisplay = 0; 35475718399fSFrançois Tigeot if (vdisplay < 0) 35485718399fSFrançois Tigeot vdisplay = 0; 35495718399fSFrançois Tigeot 35505718399fSFrançois Tigeot for (i = 0; i < count; i++) { 3551ce3d36d7SFrançois Tigeot const struct drm_display_mode *ptr = &drm_dmt_modes[i]; 35525718399fSFrançois Tigeot if (hdisplay && vdisplay) { 35535718399fSFrançois Tigeot /* 35545718399fSFrançois Tigeot * Only when two are valid, they will be used to check 35555718399fSFrançois Tigeot * whether the mode should be added to the mode list of 35565718399fSFrançois Tigeot * the connector. 35575718399fSFrançois Tigeot */ 35585718399fSFrançois Tigeot if (ptr->hdisplay > hdisplay || 35595718399fSFrançois Tigeot ptr->vdisplay > vdisplay) 35605718399fSFrançois Tigeot continue; 35615718399fSFrançois Tigeot } 35625718399fSFrançois Tigeot if (drm_mode_vrefresh(ptr) > 61) 35635718399fSFrançois Tigeot continue; 35645718399fSFrançois Tigeot mode = drm_mode_duplicate(dev, ptr); 35655718399fSFrançois Tigeot if (mode) { 35665718399fSFrançois Tigeot drm_mode_probed_add(connector, mode); 35675718399fSFrançois Tigeot num_modes++; 35685718399fSFrançois Tigeot } 35695718399fSFrançois Tigeot } 35705718399fSFrançois Tigeot return num_modes; 35715718399fSFrançois Tigeot } 35726e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_modes_noedid); 357387cc1051SMichael Neumann 3574*9edbd4a0SFrançois Tigeot void drm_set_preferred_mode(struct drm_connector *connector, 3575*9edbd4a0SFrançois Tigeot int hpref, int vpref) 3576*9edbd4a0SFrançois Tigeot { 3577*9edbd4a0SFrançois Tigeot struct drm_display_mode *mode; 3578*9edbd4a0SFrançois Tigeot 3579*9edbd4a0SFrançois Tigeot list_for_each_entry(mode, &connector->probed_modes, head) { 3580*9edbd4a0SFrançois Tigeot if (drm_mode_width(mode) == hpref && 3581*9edbd4a0SFrançois Tigeot drm_mode_height(mode) == vpref) 3582*9edbd4a0SFrançois Tigeot mode->type |= DRM_MODE_TYPE_PREFERRED; 3583*9edbd4a0SFrançois Tigeot } 3584*9edbd4a0SFrançois Tigeot } 3585*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_set_preferred_mode); 3586*9edbd4a0SFrançois Tigeot 358787cc1051SMichael Neumann /** 358887cc1051SMichael Neumann * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with 358987cc1051SMichael Neumann * data from a DRM display mode 359087cc1051SMichael Neumann * @frame: HDMI AVI infoframe 359187cc1051SMichael Neumann * @mode: DRM display mode 359287cc1051SMichael Neumann * 359387cc1051SMichael Neumann * Returns 0 on success or a negative error code on failure. 359487cc1051SMichael Neumann */ 359587cc1051SMichael Neumann int 359687cc1051SMichael Neumann drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, 359787cc1051SMichael Neumann const struct drm_display_mode *mode) 359887cc1051SMichael Neumann { 359987cc1051SMichael Neumann int err; 360087cc1051SMichael Neumann 360187cc1051SMichael Neumann if (!frame || !mode) 360287cc1051SMichael Neumann return -EINVAL; 360387cc1051SMichael Neumann 360487cc1051SMichael Neumann err = hdmi_avi_infoframe_init(frame); 360587cc1051SMichael Neumann if (err < 0) 360687cc1051SMichael Neumann return err; 360787cc1051SMichael Neumann 3608*9edbd4a0SFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLCLK) 3609*9edbd4a0SFrançois Tigeot frame->pixel_repeat = 1; 3610*9edbd4a0SFrançois Tigeot 361187cc1051SMichael Neumann frame->video_code = drm_match_cea_mode(mode); 361287cc1051SMichael Neumann 361387cc1051SMichael Neumann frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; 361487cc1051SMichael Neumann frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; 361587cc1051SMichael Neumann 361687cc1051SMichael Neumann return 0; 361787cc1051SMichael Neumann } 361887cc1051SMichael Neumann EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); 3619*9edbd4a0SFrançois Tigeot 3620*9edbd4a0SFrançois Tigeot static enum hdmi_3d_structure 3621*9edbd4a0SFrançois Tigeot s3d_structure_from_display_mode(const struct drm_display_mode *mode) 3622*9edbd4a0SFrançois Tigeot { 3623*9edbd4a0SFrançois Tigeot u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK; 3624*9edbd4a0SFrançois Tigeot 3625*9edbd4a0SFrançois Tigeot switch (layout) { 3626*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FRAME_PACKING: 3627*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FRAME_PACKING; 3628*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: 3629*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE; 3630*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: 3631*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE; 3632*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: 3633*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL; 3634*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH: 3635*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH; 3636*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: 3637*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; 3638*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: 3639*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM; 3640*9edbd4a0SFrançois Tigeot case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: 3641*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF; 3642*9edbd4a0SFrançois Tigeot default: 3643*9edbd4a0SFrançois Tigeot return HDMI_3D_STRUCTURE_INVALID; 3644*9edbd4a0SFrançois Tigeot } 3645*9edbd4a0SFrançois Tigeot } 3646*9edbd4a0SFrançois Tigeot 3647*9edbd4a0SFrançois Tigeot /** 3648*9edbd4a0SFrançois Tigeot * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with 3649*9edbd4a0SFrançois Tigeot * data from a DRM display mode 3650*9edbd4a0SFrançois Tigeot * @frame: HDMI vendor infoframe 3651*9edbd4a0SFrançois Tigeot * @mode: DRM display mode 3652*9edbd4a0SFrançois Tigeot * 3653*9edbd4a0SFrançois Tigeot * Note that there's is a need to send HDMI vendor infoframes only when using a 3654*9edbd4a0SFrançois Tigeot * 4k or stereoscopic 3D mode. So when giving any other mode as input this 3655*9edbd4a0SFrançois Tigeot * function will return -EINVAL, error that can be safely ignored. 3656*9edbd4a0SFrançois Tigeot * 3657*9edbd4a0SFrançois Tigeot * Returns 0 on success or a negative error code on failure. 3658*9edbd4a0SFrançois Tigeot */ 3659*9edbd4a0SFrançois Tigeot int 3660*9edbd4a0SFrançois Tigeot drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, 3661*9edbd4a0SFrançois Tigeot const struct drm_display_mode *mode) 3662*9edbd4a0SFrançois Tigeot { 3663*9edbd4a0SFrançois Tigeot int err; 3664*9edbd4a0SFrançois Tigeot u32 s3d_flags; 3665*9edbd4a0SFrançois Tigeot u8 vic; 3666*9edbd4a0SFrançois Tigeot 3667*9edbd4a0SFrançois Tigeot if (!frame || !mode) 3668*9edbd4a0SFrançois Tigeot return -EINVAL; 3669*9edbd4a0SFrançois Tigeot 3670*9edbd4a0SFrançois Tigeot vic = drm_match_hdmi_mode(mode); 3671*9edbd4a0SFrançois Tigeot s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; 3672*9edbd4a0SFrançois Tigeot 3673*9edbd4a0SFrançois Tigeot if (!vic && !s3d_flags) 3674*9edbd4a0SFrançois Tigeot return -EINVAL; 3675*9edbd4a0SFrançois Tigeot 3676*9edbd4a0SFrançois Tigeot if (vic && s3d_flags) 3677*9edbd4a0SFrançois Tigeot return -EINVAL; 3678*9edbd4a0SFrançois Tigeot 3679*9edbd4a0SFrançois Tigeot err = hdmi_vendor_infoframe_init(frame); 3680*9edbd4a0SFrançois Tigeot if (err < 0) 3681*9edbd4a0SFrançois Tigeot return err; 3682*9edbd4a0SFrançois Tigeot 3683*9edbd4a0SFrançois Tigeot if (vic) 3684*9edbd4a0SFrançois Tigeot frame->vic = vic; 3685*9edbd4a0SFrançois Tigeot else 3686*9edbd4a0SFrançois Tigeot frame->s3d_struct = s3d_structure_from_display_mode(mode); 3687*9edbd4a0SFrançois Tigeot 3688*9edbd4a0SFrançois Tigeot return 0; 3689*9edbd4a0SFrançois Tigeot } 3690*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); 3691