xref: /dflybsd-src/sys/dev/drm/drm_edid.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
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  */
301e12ee3bSFrançois Tigeot 
311e12ee3bSFrançois Tigeot #ifdef __DragonFly__
32610e3300SPeeter Must #include "opt_drm.h"
331e12ee3bSFrançois Tigeot #endif
34610e3300SPeeter Must 
356e29dde8SFrançois Tigeot #include <linux/kernel.h>
361e12ee3bSFrançois Tigeot #include <linux/slab.h>
3787cc1051SMichael Neumann #include <linux/hdmi.h>
386e29dde8SFrançois Tigeot #include <linux/i2c.h>
39d82bf20eSFrançois Tigeot #include <linux/module.h>
40b8e997a6SFrançois Tigeot #include <linux/vga_switcheroo.h>
4118e26a6dSFrançois Tigeot #include <drm/drmP.h>
4218e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
43a85cb24fSFrançois Tigeot #include <drm/drm_encoder.h>
448621f407SFrançois Tigeot #include <drm/drm_displayid.h>
45a85cb24fSFrançois Tigeot #include <drm/drm_scdc_helper.h>
46a85cb24fSFrançois Tigeot 
47a85cb24fSFrançois Tigeot #include "drm_crtc_internal.h"
48d82bf20eSFrançois Tigeot 
495718399fSFrançois Tigeot #define version_greater(edid, maj, min) \
505718399fSFrançois Tigeot 	(((edid)->version > (maj)) || \
515718399fSFrançois Tigeot 	 ((edid)->version == (maj) && (edid)->revision > (min)))
525718399fSFrançois Tigeot 
535718399fSFrançois Tigeot #define EDID_EST_TIMINGS 16
545718399fSFrançois Tigeot #define EDID_STD_TIMINGS 8
555718399fSFrançois Tigeot #define EDID_DETAILED_TIMINGS 4
565718399fSFrançois Tigeot 
575718399fSFrançois Tigeot /*
585718399fSFrançois Tigeot  * EDID blocks out in the wild have a variety of bugs, try to collect
595718399fSFrançois Tigeot  * them here (note that userspace may work around broken monitors first,
605718399fSFrançois Tigeot  * but fixes should make their way here so that the kernel "just works"
615718399fSFrançois Tigeot  * on as many displays as possible).
625718399fSFrançois Tigeot  */
635718399fSFrançois Tigeot 
645718399fSFrançois Tigeot /* First detailed mode wrong, use largest 60Hz mode */
655718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_60		(1 << 0)
665718399fSFrançois Tigeot /* Reported 135MHz pixel clock is too high, needs adjustment */
675718399fSFrançois Tigeot #define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1)
685718399fSFrançois Tigeot /* Prefer the largest mode at 75 Hz */
695718399fSFrançois Tigeot #define EDID_QUIRK_PREFER_LARGE_75		(1 << 2)
705718399fSFrançois Tigeot /* Detail timing is in cm not mm */
715718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_IN_CM		(1 << 3)
725718399fSFrançois Tigeot /* Detailed timing descriptors have bogus size values, so just take the
735718399fSFrançois Tigeot  * maximum size and use that.
745718399fSFrançois Tigeot  */
755718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4)
765718399fSFrançois Tigeot /* Monitor forgot to set the first detailed is preferred bit. */
775718399fSFrançois Tigeot #define EDID_QUIRK_FIRST_DETAILED_PREFERRED	(1 << 5)
785718399fSFrançois Tigeot /* use +hsync +vsync for detailed mode */
795718399fSFrançois Tigeot #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
806e29dde8SFrançois Tigeot /* Force reduced-blanking timings for detailed modes */
816e29dde8SFrançois Tigeot #define EDID_QUIRK_FORCE_REDUCED_BLANKING	(1 << 7)
829edbd4a0SFrançois Tigeot /* Force 8bpc */
839edbd4a0SFrançois Tigeot #define EDID_QUIRK_FORCE_8BPC			(1 << 8)
84ba55f2f5SFrançois Tigeot /* Force 12bpc */
85ba55f2f5SFrançois Tigeot #define EDID_QUIRK_FORCE_12BPC			(1 << 9)
868621f407SFrançois Tigeot /* Force 6bpc */
878621f407SFrançois Tigeot #define EDID_QUIRK_FORCE_6BPC			(1 << 10)
88a85cb24fSFrançois Tigeot /* Force 10bpc */
89a85cb24fSFrançois Tigeot #define EDID_QUIRK_FORCE_10BPC			(1 << 11)
90*3f2dd94aSFrançois Tigeot /* Non desktop display (i.e. HMD) */
91*3f2dd94aSFrançois Tigeot #define EDID_QUIRK_NON_DESKTOP			(1 << 12)
925718399fSFrançois Tigeot 
935718399fSFrançois Tigeot struct detailed_mode_closure {
945718399fSFrançois Tigeot 	struct drm_connector *connector;
955718399fSFrançois Tigeot 	struct edid *edid;
965718399fSFrançois Tigeot 	bool preferred;
975718399fSFrançois Tigeot 	u32 quirks;
985718399fSFrançois Tigeot 	int modes;
995718399fSFrançois Tigeot };
1005718399fSFrançois Tigeot 
1015718399fSFrançois Tigeot #define LEVEL_DMT	0
1025718399fSFrançois Tigeot #define LEVEL_GTF	1
1035718399fSFrançois Tigeot #define LEVEL_GTF2	2
1045718399fSFrançois Tigeot #define LEVEL_CVT	3
1055718399fSFrançois Tigeot 
1064be47400SFrançois Tigeot static const struct edid_quirk {
1076e29dde8SFrançois Tigeot 	char vendor[4];
1085718399fSFrançois Tigeot 	int product_id;
1095718399fSFrançois Tigeot 	u32 quirks;
1105718399fSFrançois Tigeot } edid_quirk_list[] = {
1115718399fSFrançois Tigeot 	/* Acer AL1706 */
1125718399fSFrançois Tigeot 	{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
1135718399fSFrançois Tigeot 	/* Acer F51 */
1145718399fSFrançois Tigeot 	{ "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
1155718399fSFrançois Tigeot 	/* Unknown Acer */
1165718399fSFrançois Tigeot 	{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
1175718399fSFrançois Tigeot 
1188621f407SFrançois Tigeot 	/* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
1198621f407SFrançois Tigeot 	{ "AEO", 0, EDID_QUIRK_FORCE_6BPC },
1208621f407SFrançois Tigeot 
121*3f2dd94aSFrançois Tigeot 	/* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */
122*3f2dd94aSFrançois Tigeot 	{ "CPT", 0x17df, EDID_QUIRK_FORCE_6BPC },
123*3f2dd94aSFrançois Tigeot 
1245718399fSFrançois Tigeot 	/* Belinea 10 15 55 */
1255718399fSFrançois Tigeot 	{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
1265718399fSFrançois Tigeot 	{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
1275718399fSFrançois Tigeot 
1285718399fSFrançois Tigeot 	/* Envision Peripherals, Inc. EN-7100e */
1295718399fSFrançois Tigeot 	{ "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
1305718399fSFrançois Tigeot 	/* Envision EN2028 */
1315718399fSFrançois Tigeot 	{ "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 },
1325718399fSFrançois Tigeot 
1335718399fSFrançois Tigeot 	/* Funai Electronics PM36B */
1345718399fSFrançois Tigeot 	{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
1355718399fSFrançois Tigeot 	  EDID_QUIRK_DETAILED_IN_CM },
1365718399fSFrançois Tigeot 
137a85cb24fSFrançois Tigeot 	/* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */
138a85cb24fSFrançois Tigeot 	{ "LGD", 764, EDID_QUIRK_FORCE_10BPC },
139a85cb24fSFrançois Tigeot 
1405718399fSFrançois Tigeot 	/* LG Philips LCD LP154W01-A5 */
1415718399fSFrançois Tigeot 	{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
1425718399fSFrançois Tigeot 	{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
1435718399fSFrançois Tigeot 
1445718399fSFrançois Tigeot 	/* Philips 107p5 CRT */
1455718399fSFrançois Tigeot 	{ "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
1465718399fSFrançois Tigeot 
1475718399fSFrançois Tigeot 	/* Proview AY765C */
1485718399fSFrançois Tigeot 	{ "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
1495718399fSFrançois Tigeot 
1505718399fSFrançois Tigeot 	/* Samsung SyncMaster 205BW.  Note: irony */
1515718399fSFrançois Tigeot 	{ "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
1525718399fSFrançois Tigeot 	/* Samsung SyncMaster 22[5-6]BW */
1535718399fSFrançois Tigeot 	{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
1545718399fSFrançois Tigeot 	{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
1556e29dde8SFrançois Tigeot 
156ba55f2f5SFrançois Tigeot 	/* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
157ba55f2f5SFrançois Tigeot 	{ "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC },
158ba55f2f5SFrançois Tigeot 
1596e29dde8SFrançois Tigeot 	/* ViewSonic VA2026w */
1606e29dde8SFrançois Tigeot 	{ "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
1619edbd4a0SFrançois Tigeot 
1629edbd4a0SFrançois Tigeot 	/* Medion MD 30217 PG */
1639edbd4a0SFrançois Tigeot 	{ "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 },
1649edbd4a0SFrançois Tigeot 
1659edbd4a0SFrançois Tigeot 	/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
1669edbd4a0SFrançois Tigeot 	{ "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC },
1674be47400SFrançois Tigeot 
1684be47400SFrançois Tigeot 	/* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
1694be47400SFrançois Tigeot 	{ "ETR", 13896, EDID_QUIRK_FORCE_8BPC },
170*3f2dd94aSFrançois Tigeot 
171*3f2dd94aSFrançois Tigeot 	/* HTC Vive VR Headset */
172*3f2dd94aSFrançois Tigeot 	{ "HVR", 0xaa01, EDID_QUIRK_NON_DESKTOP },
1735718399fSFrançois Tigeot };
1745718399fSFrançois Tigeot 
175d82bf20eSFrançois Tigeot /*
176d82bf20eSFrançois Tigeot  * Autogenerated from the DMT spec.
177d82bf20eSFrançois Tigeot  * This table is copied from xfree86/modes/xf86EdidModes.c.
178d82bf20eSFrançois Tigeot  */
179d82bf20eSFrançois Tigeot static const struct drm_display_mode drm_dmt_modes[] = {
18019c468b4SFrançois Tigeot 	/* 0x01 - 640x350@85Hz */
181d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
182d82bf20eSFrançois Tigeot 		   736, 832, 0, 350, 382, 385, 445, 0,
183d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
18419c468b4SFrançois Tigeot 	/* 0x02 - 640x400@85Hz */
185d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
186d82bf20eSFrançois Tigeot 		   736, 832, 0, 400, 401, 404, 445, 0,
187d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
18819c468b4SFrançois Tigeot 	/* 0x03 - 720x400@85Hz */
189d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
190d82bf20eSFrançois Tigeot 		   828, 936, 0, 400, 401, 404, 446, 0,
191d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
19219c468b4SFrançois Tigeot 	/* 0x04 - 640x480@60Hz */
193d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
19419c468b4SFrançois Tigeot 		   752, 800, 0, 480, 490, 492, 525, 0,
195d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
19619c468b4SFrançois Tigeot 	/* 0x05 - 640x480@72Hz */
197d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
198d82bf20eSFrançois Tigeot 		   704, 832, 0, 480, 489, 492, 520, 0,
199d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
20019c468b4SFrançois Tigeot 	/* 0x06 - 640x480@75Hz */
201d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
202d82bf20eSFrançois Tigeot 		   720, 840, 0, 480, 481, 484, 500, 0,
203d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
20419c468b4SFrançois Tigeot 	/* 0x07 - 640x480@85Hz */
205d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
206d82bf20eSFrançois Tigeot 		   752, 832, 0, 480, 481, 484, 509, 0,
207d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
20819c468b4SFrançois Tigeot 	/* 0x08 - 800x600@56Hz */
209d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
210d82bf20eSFrançois Tigeot 		   896, 1024, 0, 600, 601, 603, 625, 0,
211d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
21219c468b4SFrançois Tigeot 	/* 0x09 - 800x600@60Hz */
213d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
214d82bf20eSFrançois Tigeot 		   968, 1056, 0, 600, 601, 605, 628, 0,
215d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
21619c468b4SFrançois Tigeot 	/* 0x0a - 800x600@72Hz */
217d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
218d82bf20eSFrançois Tigeot 		   976, 1040, 0, 600, 637, 643, 666, 0,
219d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
22019c468b4SFrançois Tigeot 	/* 0x0b - 800x600@75Hz */
221d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
222d82bf20eSFrançois Tigeot 		   896, 1056, 0, 600, 601, 604, 625, 0,
223d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
22419c468b4SFrançois Tigeot 	/* 0x0c - 800x600@85Hz */
225d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
226d82bf20eSFrançois Tigeot 		   896, 1048, 0, 600, 601, 604, 631, 0,
227d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
22819c468b4SFrançois Tigeot 	/* 0x0d - 800x600@120Hz RB */
229d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848,
230d82bf20eSFrançois Tigeot 		   880, 960, 0, 600, 603, 607, 636, 0,
231d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
23219c468b4SFrançois Tigeot 	/* 0x0e - 848x480@60Hz */
233d82bf20eSFrançois Tigeot 	{ DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
234d82bf20eSFrançois Tigeot 		   976, 1088, 0, 480, 486, 494, 517, 0,
235d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
23619c468b4SFrançois Tigeot 	/* 0x0f - 1024x768@43Hz, interlace */
237d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
238c0e85e96SFrançois Tigeot 		   1208, 1264, 0, 768, 768, 776, 817, 0,
239d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
240d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_INTERLACE) },
24119c468b4SFrançois Tigeot 	/* 0x10 - 1024x768@60Hz */
242d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
243d82bf20eSFrançois Tigeot 		   1184, 1344, 0, 768, 771, 777, 806, 0,
244d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
24519c468b4SFrançois Tigeot 	/* 0x11 - 1024x768@70Hz */
246d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
247d82bf20eSFrançois Tigeot 		   1184, 1328, 0, 768, 771, 777, 806, 0,
248d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
24919c468b4SFrançois Tigeot 	/* 0x12 - 1024x768@75Hz */
250d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
251d82bf20eSFrançois Tigeot 		   1136, 1312, 0, 768, 769, 772, 800, 0,
252d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
25319c468b4SFrançois Tigeot 	/* 0x13 - 1024x768@85Hz */
254d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
255d82bf20eSFrançois Tigeot 		   1168, 1376, 0, 768, 769, 772, 808, 0,
256d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
25719c468b4SFrançois Tigeot 	/* 0x14 - 1024x768@120Hz RB */
258d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072,
259d82bf20eSFrançois Tigeot 		   1104, 1184, 0, 768, 771, 775, 813, 0,
260d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
26119c468b4SFrançois Tigeot 	/* 0x15 - 1152x864@75Hz */
262d82bf20eSFrançois Tigeot 	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
263d82bf20eSFrançois Tigeot 		   1344, 1600, 0, 864, 865, 868, 900, 0,
264d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
26519c468b4SFrançois Tigeot 	/* 0x55 - 1280x720@60Hz */
26619c468b4SFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
26719c468b4SFrançois Tigeot 		   1430, 1650, 0, 720, 725, 730, 750, 0,
26819c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
26919c468b4SFrançois Tigeot 	/* 0x16 - 1280x768@60Hz RB */
270d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328,
271d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 768, 771, 778, 790, 0,
272d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
27319c468b4SFrançois Tigeot 	/* 0x17 - 1280x768@60Hz */
274d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
275d82bf20eSFrançois Tigeot 		   1472, 1664, 0, 768, 771, 778, 798, 0,
276d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
27719c468b4SFrançois Tigeot 	/* 0x18 - 1280x768@75Hz */
278d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
279d82bf20eSFrançois Tigeot 		   1488, 1696, 0, 768, 771, 778, 805, 0,
28019c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
28119c468b4SFrançois Tigeot 	/* 0x19 - 1280x768@85Hz */
282d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
283d82bf20eSFrançois Tigeot 		   1496, 1712, 0, 768, 771, 778, 809, 0,
284d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
28519c468b4SFrançois Tigeot 	/* 0x1a - 1280x768@120Hz RB */
286d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328,
287d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 768, 771, 778, 813, 0,
288d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
28919c468b4SFrançois Tigeot 	/* 0x1b - 1280x800@60Hz RB */
290d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328,
291d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 800, 803, 809, 823, 0,
292d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
29319c468b4SFrançois Tigeot 	/* 0x1c - 1280x800@60Hz */
294d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
295d82bf20eSFrançois Tigeot 		   1480, 1680, 0, 800, 803, 809, 831, 0,
29619c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
29719c468b4SFrançois Tigeot 	/* 0x1d - 1280x800@75Hz */
298d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
299d82bf20eSFrançois Tigeot 		   1488, 1696, 0, 800, 803, 809, 838, 0,
300d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
30119c468b4SFrançois Tigeot 	/* 0x1e - 1280x800@85Hz */
302d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
303d82bf20eSFrançois Tigeot 		   1496, 1712, 0, 800, 803, 809, 843, 0,
304d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
30519c468b4SFrançois Tigeot 	/* 0x1f - 1280x800@120Hz RB */
306d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328,
307d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 800, 803, 809, 847, 0,
308d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
30919c468b4SFrançois Tigeot 	/* 0x20 - 1280x960@60Hz */
310d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
311d82bf20eSFrançois Tigeot 		   1488, 1800, 0, 960, 961, 964, 1000, 0,
312d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
31319c468b4SFrançois Tigeot 	/* 0x21 - 1280x960@85Hz */
314d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
315d82bf20eSFrançois Tigeot 		   1504, 1728, 0, 960, 961, 964, 1011, 0,
316d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
31719c468b4SFrançois Tigeot 	/* 0x22 - 1280x960@120Hz RB */
318d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328,
319d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 960, 963, 967, 1017, 0,
320d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
32119c468b4SFrançois Tigeot 	/* 0x23 - 1280x1024@60Hz */
322d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
323d82bf20eSFrançois Tigeot 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
324d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
32519c468b4SFrançois Tigeot 	/* 0x24 - 1280x1024@75Hz */
326d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
327d82bf20eSFrançois Tigeot 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
328d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
32919c468b4SFrançois Tigeot 	/* 0x25 - 1280x1024@85Hz */
330d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
331d82bf20eSFrançois Tigeot 		   1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
332d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
33319c468b4SFrançois Tigeot 	/* 0x26 - 1280x1024@120Hz RB */
334d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328,
335d82bf20eSFrançois Tigeot 		   1360, 1440, 0, 1024, 1027, 1034, 1084, 0,
336d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
33719c468b4SFrançois Tigeot 	/* 0x27 - 1360x768@60Hz */
338d82bf20eSFrançois Tigeot 	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
339d82bf20eSFrançois Tigeot 		   1536, 1792, 0, 768, 771, 777, 795, 0,
340d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
34119c468b4SFrançois Tigeot 	/* 0x28 - 1360x768@120Hz RB */
342d82bf20eSFrançois Tigeot 	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408,
343d82bf20eSFrançois Tigeot 		   1440, 1520, 0, 768, 771, 776, 813, 0,
344d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
34519c468b4SFrançois Tigeot 	/* 0x51 - 1366x768@60Hz */
34619c468b4SFrançois Tigeot 	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 85500, 1366, 1436,
34719c468b4SFrançois Tigeot 		   1579, 1792, 0, 768, 771, 774, 798, 0,
34819c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
34919c468b4SFrançois Tigeot 	/* 0x56 - 1366x768@60Hz */
35019c468b4SFrançois Tigeot 	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 72000, 1366, 1380,
35119c468b4SFrançois Tigeot 		   1436, 1500, 0, 768, 769, 772, 800, 0,
35219c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
35319c468b4SFrançois Tigeot 	/* 0x29 - 1400x1050@60Hz RB */
354d82bf20eSFrançois Tigeot 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448,
355d82bf20eSFrançois Tigeot 		   1480, 1560, 0, 1050, 1053, 1057, 1080, 0,
356d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
35719c468b4SFrançois Tigeot 	/* 0x2a - 1400x1050@60Hz */
358d82bf20eSFrançois Tigeot 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
359d82bf20eSFrançois Tigeot 		   1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
360d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36119c468b4SFrançois Tigeot 	/* 0x2b - 1400x1050@75Hz */
362d82bf20eSFrançois Tigeot 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
363d82bf20eSFrançois Tigeot 		   1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
364d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36519c468b4SFrançois Tigeot 	/* 0x2c - 1400x1050@85Hz */
366d82bf20eSFrançois Tigeot 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
367d82bf20eSFrançois Tigeot 		   1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
368d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36919c468b4SFrançois Tigeot 	/* 0x2d - 1400x1050@120Hz RB */
370d82bf20eSFrançois Tigeot 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448,
371d82bf20eSFrançois Tigeot 		   1480, 1560, 0, 1050, 1053, 1057, 1112, 0,
372d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
37319c468b4SFrançois Tigeot 	/* 0x2e - 1440x900@60Hz RB */
374d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488,
375d82bf20eSFrançois Tigeot 		   1520, 1600, 0, 900, 903, 909, 926, 0,
376d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
37719c468b4SFrançois Tigeot 	/* 0x2f - 1440x900@60Hz */
378d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
379d82bf20eSFrançois Tigeot 		   1672, 1904, 0, 900, 903, 909, 934, 0,
380d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
38119c468b4SFrançois Tigeot 	/* 0x30 - 1440x900@75Hz */
382d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
383d82bf20eSFrançois Tigeot 		   1688, 1936, 0, 900, 903, 909, 942, 0,
384d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
38519c468b4SFrançois Tigeot 	/* 0x31 - 1440x900@85Hz */
386d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
387d82bf20eSFrançois Tigeot 		   1696, 1952, 0, 900, 903, 909, 948, 0,
388d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
38919c468b4SFrançois Tigeot 	/* 0x32 - 1440x900@120Hz RB */
390d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488,
391d82bf20eSFrançois Tigeot 		   1520, 1600, 0, 900, 903, 909, 953, 0,
392d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
39319c468b4SFrançois Tigeot 	/* 0x53 - 1600x900@60Hz */
39419c468b4SFrançois Tigeot 	{ DRM_MODE("1600x900", DRM_MODE_TYPE_DRIVER, 108000, 1600, 1624,
39519c468b4SFrançois Tigeot 		   1704, 1800, 0, 900, 901, 904, 1000, 0,
39619c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
39719c468b4SFrançois Tigeot 	/* 0x33 - 1600x1200@60Hz */
398d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
399d82bf20eSFrançois Tigeot 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
400d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
40119c468b4SFrançois Tigeot 	/* 0x34 - 1600x1200@65Hz */
402d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
403d82bf20eSFrançois Tigeot 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
404d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
40519c468b4SFrançois Tigeot 	/* 0x35 - 1600x1200@70Hz */
406d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
407d82bf20eSFrançois Tigeot 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
408d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
40919c468b4SFrançois Tigeot 	/* 0x36 - 1600x1200@75Hz */
410d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
411d82bf20eSFrançois Tigeot 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
412d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
41319c468b4SFrançois Tigeot 	/* 0x37 - 1600x1200@85Hz */
414d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
415d82bf20eSFrançois Tigeot 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
416d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
41719c468b4SFrançois Tigeot 	/* 0x38 - 1600x1200@120Hz RB */
418d82bf20eSFrançois Tigeot 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648,
419d82bf20eSFrançois Tigeot 		   1680, 1760, 0, 1200, 1203, 1207, 1271, 0,
420d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
42119c468b4SFrançois Tigeot 	/* 0x39 - 1680x1050@60Hz RB */
422d82bf20eSFrançois Tigeot 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728,
423d82bf20eSFrançois Tigeot 		   1760, 1840, 0, 1050, 1053, 1059, 1080, 0,
424d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
42519c468b4SFrançois Tigeot 	/* 0x3a - 1680x1050@60Hz */
426d82bf20eSFrançois Tigeot 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
427d82bf20eSFrançois Tigeot 		   1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
428d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
42919c468b4SFrançois Tigeot 	/* 0x3b - 1680x1050@75Hz */
430d82bf20eSFrançois Tigeot 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
431d82bf20eSFrançois Tigeot 		   1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
432d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
43319c468b4SFrançois Tigeot 	/* 0x3c - 1680x1050@85Hz */
434d82bf20eSFrançois Tigeot 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
435d82bf20eSFrançois Tigeot 		   1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
436d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
43719c468b4SFrançois Tigeot 	/* 0x3d - 1680x1050@120Hz RB */
438d82bf20eSFrançois Tigeot 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728,
439d82bf20eSFrançois Tigeot 		   1760, 1840, 0, 1050, 1053, 1059, 1112, 0,
440d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
44119c468b4SFrançois Tigeot 	/* 0x3e - 1792x1344@60Hz */
442d82bf20eSFrançois Tigeot 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
443d82bf20eSFrançois Tigeot 		   2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
444d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44519c468b4SFrançois Tigeot 	/* 0x3f - 1792x1344@75Hz */
446d82bf20eSFrançois Tigeot 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
447d82bf20eSFrançois Tigeot 		   2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
448d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44919c468b4SFrançois Tigeot 	/* 0x40 - 1792x1344@120Hz RB */
450d82bf20eSFrançois Tigeot 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840,
451d82bf20eSFrançois Tigeot 		   1872, 1952, 0, 1344, 1347, 1351, 1423, 0,
452d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
45319c468b4SFrançois Tigeot 	/* 0x41 - 1856x1392@60Hz */
454d82bf20eSFrançois Tigeot 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
455d82bf20eSFrançois Tigeot 		   2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
456d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
45719c468b4SFrançois Tigeot 	/* 0x42 - 1856x1392@75Hz */
458d82bf20eSFrançois Tigeot 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
45919c468b4SFrançois Tigeot 		   2208, 2560, 0, 1392, 1393, 1396, 1500, 0,
460d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
46119c468b4SFrançois Tigeot 	/* 0x43 - 1856x1392@120Hz RB */
462d82bf20eSFrançois Tigeot 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904,
463d82bf20eSFrançois Tigeot 		   1936, 2016, 0, 1392, 1395, 1399, 1474, 0,
464d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
46519c468b4SFrançois Tigeot 	/* 0x52 - 1920x1080@60Hz */
46619c468b4SFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
46719c468b4SFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
46819c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
46919c468b4SFrançois Tigeot 	/* 0x44 - 1920x1200@60Hz RB */
470d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968,
471d82bf20eSFrançois Tigeot 		   2000, 2080, 0, 1200, 1203, 1209, 1235, 0,
472d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
47319c468b4SFrançois Tigeot 	/* 0x45 - 1920x1200@60Hz */
474d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
475d82bf20eSFrançois Tigeot 		   2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
476d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
47719c468b4SFrançois Tigeot 	/* 0x46 - 1920x1200@75Hz */
478d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
479d82bf20eSFrançois Tigeot 		   2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
480d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
48119c468b4SFrançois Tigeot 	/* 0x47 - 1920x1200@85Hz */
482d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
483d82bf20eSFrançois Tigeot 		   2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
484d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
48519c468b4SFrançois Tigeot 	/* 0x48 - 1920x1200@120Hz RB */
486d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968,
487d82bf20eSFrançois Tigeot 		   2000, 2080, 0, 1200, 1203, 1209, 1271, 0,
488d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
48919c468b4SFrançois Tigeot 	/* 0x49 - 1920x1440@60Hz */
490d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
491d82bf20eSFrançois Tigeot 		   2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
492d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49319c468b4SFrançois Tigeot 	/* 0x4a - 1920x1440@75Hz */
494d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
495d82bf20eSFrançois Tigeot 		   2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
496d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49719c468b4SFrançois Tigeot 	/* 0x4b - 1920x1440@120Hz RB */
498d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968,
499d82bf20eSFrançois Tigeot 		   2000, 2080, 0, 1440, 1443, 1447, 1525, 0,
500d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
50119c468b4SFrançois Tigeot 	/* 0x54 - 2048x1152@60Hz */
50219c468b4SFrançois Tigeot 	{ DRM_MODE("2048x1152", DRM_MODE_TYPE_DRIVER, 162000, 2048, 2074,
50319c468b4SFrançois Tigeot 		   2154, 2250, 0, 1152, 1153, 1156, 1200, 0,
50419c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
50519c468b4SFrançois Tigeot 	/* 0x4c - 2560x1600@60Hz RB */
506d82bf20eSFrançois Tigeot 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608,
507d82bf20eSFrançois Tigeot 		   2640, 2720, 0, 1600, 1603, 1609, 1646, 0,
508d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
50919c468b4SFrançois Tigeot 	/* 0x4d - 2560x1600@60Hz */
510d82bf20eSFrançois Tigeot 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
511d82bf20eSFrançois Tigeot 		   3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
512d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
51319c468b4SFrançois Tigeot 	/* 0x4e - 2560x1600@75Hz */
514d82bf20eSFrançois Tigeot 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
515d82bf20eSFrançois Tigeot 		   3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
516d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
51719c468b4SFrançois Tigeot 	/* 0x4f - 2560x1600@85Hz */
518d82bf20eSFrançois Tigeot 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
519d82bf20eSFrançois Tigeot 		   3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
520d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
52119c468b4SFrançois Tigeot 	/* 0x50 - 2560x1600@120Hz RB */
522d82bf20eSFrançois Tigeot 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608,
523d82bf20eSFrançois Tigeot 		   2640, 2720, 0, 1600, 1603, 1609, 1694, 0,
524d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
52519c468b4SFrançois Tigeot 	/* 0x57 - 4096x2160@60Hz RB */
52619c468b4SFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556744, 4096, 4104,
52719c468b4SFrançois Tigeot 		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
52819c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
52919c468b4SFrançois Tigeot 	/* 0x58 - 4096x2160@59.94Hz RB */
53019c468b4SFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104,
53119c468b4SFrançois Tigeot 		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
53219c468b4SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
533d82bf20eSFrançois Tigeot };
534d82bf20eSFrançois Tigeot 
5359edbd4a0SFrançois Tigeot /*
5369edbd4a0SFrançois Tigeot  * These more or less come from the DMT spec.  The 720x400 modes are
5379edbd4a0SFrançois Tigeot  * inferred from historical 80x25 practice.  The 640x480@67 and 832x624@75
5389edbd4a0SFrançois Tigeot  * modes are old-school Mac modes.  The EDID spec says the 1152x864@75 mode
5399edbd4a0SFrançois Tigeot  * should be 1152x870, again for the Mac, but instead we use the x864 DMT
5409edbd4a0SFrançois Tigeot  * mode.
5419edbd4a0SFrançois Tigeot  *
5429edbd4a0SFrançois Tigeot  * The DMT modes have been fact-checked; the rest are mild guesses.
5439edbd4a0SFrançois Tigeot  */
544d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_est_modes[] = {
545d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
546d82bf20eSFrançois Tigeot 		   968, 1056, 0, 600, 601, 605, 628, 0,
547d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
548d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
549d82bf20eSFrançois Tigeot 		   896, 1024, 0, 600, 601, 603,  625, 0,
550d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
551d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
552d82bf20eSFrançois Tigeot 		   720, 840, 0, 480, 481, 484, 500, 0,
553d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
554d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
555c0e85e96SFrançois Tigeot 		   704,  832, 0, 480, 489, 492, 520, 0,
556d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
557d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
558d82bf20eSFrançois Tigeot 		   768,  864, 0, 480, 483, 486, 525, 0,
559d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
560c0e85e96SFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
561d82bf20eSFrançois Tigeot 		   752, 800, 0, 480, 490, 492, 525, 0,
562d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
563d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
564d82bf20eSFrançois Tigeot 		   846, 900, 0, 400, 421, 423,  449, 0,
565d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
566d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
567d82bf20eSFrançois Tigeot 		   846,  900, 0, 400, 412, 414, 449, 0,
568d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
569d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
570d82bf20eSFrançois Tigeot 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
571d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
572c0e85e96SFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
573d82bf20eSFrançois Tigeot 		   1136, 1312, 0,  768, 769, 772, 800, 0,
574d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
575d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
576d82bf20eSFrançois Tigeot 		   1184, 1328, 0,  768, 771, 777, 806, 0,
577d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
578d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
579d82bf20eSFrançois Tigeot 		   1184, 1344, 0,  768, 771, 777, 806, 0,
580d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
581d82bf20eSFrançois Tigeot 	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
582d82bf20eSFrançois Tigeot 		   1208, 1264, 0, 768, 768, 776, 817, 0,
583d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
584d82bf20eSFrançois Tigeot 	{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
585d82bf20eSFrançois Tigeot 		   928, 1152, 0, 624, 625, 628, 667, 0,
586d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
587d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
588d82bf20eSFrançois Tigeot 		   896, 1056, 0, 600, 601, 604,  625, 0,
589d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
590d82bf20eSFrançois Tigeot 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
591d82bf20eSFrançois Tigeot 		   976, 1040, 0, 600, 637, 643, 666, 0,
592d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
593d82bf20eSFrançois Tigeot 	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
594d82bf20eSFrançois Tigeot 		   1344, 1600, 0,  864, 865, 868, 900, 0,
595d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
596d82bf20eSFrançois Tigeot };
597d82bf20eSFrançois Tigeot 
598d82bf20eSFrançois Tigeot struct minimode {
599d82bf20eSFrançois Tigeot 	short w;
600d82bf20eSFrançois Tigeot 	short h;
601d82bf20eSFrançois Tigeot 	short r;
602d82bf20eSFrançois Tigeot 	short rb;
603d82bf20eSFrançois Tigeot };
604d82bf20eSFrançois Tigeot 
605d82bf20eSFrançois Tigeot static const struct minimode est3_modes[] = {
606d82bf20eSFrançois Tigeot 	/* byte 6 */
607d82bf20eSFrançois Tigeot 	{ 640, 350, 85, 0 },
608d82bf20eSFrançois Tigeot 	{ 640, 400, 85, 0 },
609d82bf20eSFrançois Tigeot 	{ 720, 400, 85, 0 },
610d82bf20eSFrançois Tigeot 	{ 640, 480, 85, 0 },
611d82bf20eSFrançois Tigeot 	{ 848, 480, 60, 0 },
612d82bf20eSFrançois Tigeot 	{ 800, 600, 85, 0 },
613d82bf20eSFrançois Tigeot 	{ 1024, 768, 85, 0 },
614d82bf20eSFrançois Tigeot 	{ 1152, 864, 75, 0 },
615d82bf20eSFrançois Tigeot 	/* byte 7 */
616d82bf20eSFrançois Tigeot 	{ 1280, 768, 60, 1 },
617d82bf20eSFrançois Tigeot 	{ 1280, 768, 60, 0 },
618d82bf20eSFrançois Tigeot 	{ 1280, 768, 75, 0 },
619d82bf20eSFrançois Tigeot 	{ 1280, 768, 85, 0 },
620d82bf20eSFrançois Tigeot 	{ 1280, 960, 60, 0 },
621d82bf20eSFrançois Tigeot 	{ 1280, 960, 85, 0 },
622d82bf20eSFrançois Tigeot 	{ 1280, 1024, 60, 0 },
623d82bf20eSFrançois Tigeot 	{ 1280, 1024, 85, 0 },
624d82bf20eSFrançois Tigeot 	/* byte 8 */
625d82bf20eSFrançois Tigeot 	{ 1360, 768, 60, 0 },
626d82bf20eSFrançois Tigeot 	{ 1440, 900, 60, 1 },
627d82bf20eSFrançois Tigeot 	{ 1440, 900, 60, 0 },
628d82bf20eSFrançois Tigeot 	{ 1440, 900, 75, 0 },
629d82bf20eSFrançois Tigeot 	{ 1440, 900, 85, 0 },
630d82bf20eSFrançois Tigeot 	{ 1400, 1050, 60, 1 },
631d82bf20eSFrançois Tigeot 	{ 1400, 1050, 60, 0 },
632d82bf20eSFrançois Tigeot 	{ 1400, 1050, 75, 0 },
633d82bf20eSFrançois Tigeot 	/* byte 9 */
634d82bf20eSFrançois Tigeot 	{ 1400, 1050, 85, 0 },
635d82bf20eSFrançois Tigeot 	{ 1680, 1050, 60, 1 },
636d82bf20eSFrançois Tigeot 	{ 1680, 1050, 60, 0 },
637d82bf20eSFrançois Tigeot 	{ 1680, 1050, 75, 0 },
638d82bf20eSFrançois Tigeot 	{ 1680, 1050, 85, 0 },
639d82bf20eSFrançois Tigeot 	{ 1600, 1200, 60, 0 },
640d82bf20eSFrançois Tigeot 	{ 1600, 1200, 65, 0 },
641d82bf20eSFrançois Tigeot 	{ 1600, 1200, 70, 0 },
642d82bf20eSFrançois Tigeot 	/* byte 10 */
643d82bf20eSFrançois Tigeot 	{ 1600, 1200, 75, 0 },
644d82bf20eSFrançois Tigeot 	{ 1600, 1200, 85, 0 },
645d82bf20eSFrançois Tigeot 	{ 1792, 1344, 60, 0 },
6469edbd4a0SFrançois Tigeot 	{ 1792, 1344, 75, 0 },
647d82bf20eSFrançois Tigeot 	{ 1856, 1392, 60, 0 },
648d82bf20eSFrançois Tigeot 	{ 1856, 1392, 75, 0 },
649d82bf20eSFrançois Tigeot 	{ 1920, 1200, 60, 1 },
650d82bf20eSFrançois Tigeot 	{ 1920, 1200, 60, 0 },
651d82bf20eSFrançois Tigeot 	/* byte 11 */
652d82bf20eSFrançois Tigeot 	{ 1920, 1200, 75, 0 },
653d82bf20eSFrançois Tigeot 	{ 1920, 1200, 85, 0 },
654d82bf20eSFrançois Tigeot 	{ 1920, 1440, 60, 0 },
655d82bf20eSFrançois Tigeot 	{ 1920, 1440, 75, 0 },
656d82bf20eSFrançois Tigeot };
657d82bf20eSFrançois Tigeot 
658d82bf20eSFrançois Tigeot static const struct minimode extra_modes[] = {
659d82bf20eSFrançois Tigeot 	{ 1024, 576,  60, 0 },
660d82bf20eSFrançois Tigeot 	{ 1366, 768,  60, 0 },
661d82bf20eSFrançois Tigeot 	{ 1600, 900,  60, 0 },
662d82bf20eSFrançois Tigeot 	{ 1680, 945,  60, 0 },
663d82bf20eSFrançois Tigeot 	{ 1920, 1080, 60, 0 },
664d82bf20eSFrançois Tigeot 	{ 2048, 1152, 60, 0 },
665d82bf20eSFrançois Tigeot 	{ 2048, 1536, 60, 0 },
666d82bf20eSFrançois Tigeot };
667d82bf20eSFrançois Tigeot 
668d82bf20eSFrançois Tigeot /*
669d82bf20eSFrançois Tigeot  * Probably taken from CEA-861 spec.
670d82bf20eSFrançois Tigeot  * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c.
671aee94f86SFrançois Tigeot  *
672aee94f86SFrançois Tigeot  * Index using the VIC.
673d82bf20eSFrançois Tigeot  */
674d82bf20eSFrançois Tigeot static const struct drm_display_mode edid_cea_modes[] = {
675aee94f86SFrançois Tigeot 	/* 0 - dummy, VICs start at 1 */
676aee94f86SFrançois Tigeot 	{ },
677d82bf20eSFrançois Tigeot 	/* 1 - 640x480@60Hz */
678d82bf20eSFrançois Tigeot 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
679d82bf20eSFrançois Tigeot 		   752, 800, 0, 480, 490, 492, 525, 0,
6809edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
6819edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
682d82bf20eSFrançois Tigeot 	/* 2 - 720x480@60Hz */
683d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
684d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
6859edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
6869edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
687d82bf20eSFrançois Tigeot 	/* 3 - 720x480@60Hz */
688d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
689d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
6909edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
6919edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
692d82bf20eSFrançois Tigeot 	/* 4 - 1280x720@60Hz */
693d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
694d82bf20eSFrançois Tigeot 		   1430, 1650, 0, 720, 725, 730, 750, 0,
6959edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
6969edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
697d82bf20eSFrançois Tigeot 	/* 5 - 1920x1080i@60Hz */
698d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
699d82bf20eSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
700d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
7019edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
7029edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
7031b13d190SFrançois Tigeot 	/* 6 - 720(1440)x480i@60Hz */
7041b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
7051b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
706d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7079edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7089edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
7091b13d190SFrançois Tigeot 	/* 7 - 720(1440)x480i@60Hz */
7101b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
7111b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
712d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7139edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7149edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
7151b13d190SFrançois Tigeot 	/* 8 - 720(1440)x240@60Hz */
7161b13d190SFrançois Tigeot 	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
7171b13d190SFrançois Tigeot 		   801, 858, 0, 240, 244, 247, 262, 0,
718d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7199edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_DBLCLK),
7209edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
7211b13d190SFrançois Tigeot 	/* 9 - 720(1440)x240@60Hz */
7221b13d190SFrançois Tigeot 	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
7231b13d190SFrançois Tigeot 		   801, 858, 0, 240, 244, 247, 262, 0,
724d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7259edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_DBLCLK),
7269edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
727d82bf20eSFrançois Tigeot 	/* 10 - 2880x480i@60Hz */
728d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
729d82bf20eSFrançois Tigeot 		   3204, 3432, 0, 480, 488, 494, 525, 0,
730d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7319edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
7329edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
733d82bf20eSFrançois Tigeot 	/* 11 - 2880x480i@60Hz */
734d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
735d82bf20eSFrançois Tigeot 		   3204, 3432, 0, 480, 488, 494, 525, 0,
736d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7379edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
7389edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
739d82bf20eSFrançois Tigeot 	/* 12 - 2880x240@60Hz */
740d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
741d82bf20eSFrançois Tigeot 		   3204, 3432, 0, 240, 244, 247, 262, 0,
7429edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7439edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
744d82bf20eSFrançois Tigeot 	/* 13 - 2880x240@60Hz */
745d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
746d82bf20eSFrançois Tigeot 		   3204, 3432, 0, 240, 244, 247, 262, 0,
7479edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7489edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
749d82bf20eSFrançois Tigeot 	/* 14 - 1440x480@60Hz */
750d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
751d82bf20eSFrançois Tigeot 		   1596, 1716, 0, 480, 489, 495, 525, 0,
7529edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7539edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
754d82bf20eSFrançois Tigeot 	/* 15 - 1440x480@60Hz */
755d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
756d82bf20eSFrançois Tigeot 		   1596, 1716, 0, 480, 489, 495, 525, 0,
7579edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7589edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
759d82bf20eSFrançois Tigeot 	/* 16 - 1920x1080@60Hz */
760d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
761d82bf20eSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
7629edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
7639edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
764d82bf20eSFrançois Tigeot 	/* 17 - 720x576@50Hz */
765d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
766d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
7679edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7689edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
769d82bf20eSFrançois Tigeot 	/* 18 - 720x576@50Hz */
770d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
771d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
7729edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7739edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
774d82bf20eSFrançois Tigeot 	/* 19 - 1280x720@50Hz */
775d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
776d82bf20eSFrançois Tigeot 		   1760, 1980, 0, 720, 725, 730, 750, 0,
7779edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
7789edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
779d82bf20eSFrançois Tigeot 	/* 20 - 1920x1080i@50Hz */
780d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
781d82bf20eSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
782d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
7839edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
7849edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
7851b13d190SFrançois Tigeot 	/* 21 - 720(1440)x576i@50Hz */
7861b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
7871b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
788d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7899edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7909edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
7911b13d190SFrançois Tigeot 	/* 22 - 720(1440)x576i@50Hz */
7921b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
7931b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
794d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
7959edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7969edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
7971b13d190SFrançois Tigeot 	/* 23 - 720(1440)x288@50Hz */
7981b13d190SFrançois Tigeot 	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
7991b13d190SFrançois Tigeot 		   795, 864, 0, 288, 290, 293, 312, 0,
800d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
8019edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_DBLCLK),
8029edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
8031b13d190SFrançois Tigeot 	/* 24 - 720(1440)x288@50Hz */
8041b13d190SFrançois Tigeot 	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
8051b13d190SFrançois Tigeot 		   795, 864, 0, 288, 290, 293, 312, 0,
806d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
8079edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_DBLCLK),
8089edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
809d82bf20eSFrançois Tigeot 	/* 25 - 2880x576i@50Hz */
810d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
811d82bf20eSFrançois Tigeot 		   3180, 3456, 0, 576, 580, 586, 625, 0,
812d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
8139edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
8149edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
815d82bf20eSFrançois Tigeot 	/* 26 - 2880x576i@50Hz */
816d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
817d82bf20eSFrançois Tigeot 		   3180, 3456, 0, 576, 580, 586, 625, 0,
818d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
8199edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
8209edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
821d82bf20eSFrançois Tigeot 	/* 27 - 2880x288@50Hz */
822d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
823d82bf20eSFrançois Tigeot 		   3180, 3456, 0, 288, 290, 293, 312, 0,
8249edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8259edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
826d82bf20eSFrançois Tigeot 	/* 28 - 2880x288@50Hz */
827d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
828d82bf20eSFrançois Tigeot 		   3180, 3456, 0, 288, 290, 293, 312, 0,
8299edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8309edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
831d82bf20eSFrançois Tigeot 	/* 29 - 1440x576@50Hz */
832d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
833d82bf20eSFrançois Tigeot 		   1592, 1728, 0, 576, 581, 586, 625, 0,
8349edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8359edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
836d82bf20eSFrançois Tigeot 	/* 30 - 1440x576@50Hz */
837d82bf20eSFrançois Tigeot 	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
838d82bf20eSFrançois Tigeot 		   1592, 1728, 0, 576, 581, 586, 625, 0,
8399edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8409edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
841d82bf20eSFrançois Tigeot 	/* 31 - 1920x1080@50Hz */
842d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
843d82bf20eSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
8449edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8459edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
846d82bf20eSFrançois Tigeot 	/* 32 - 1920x1080@24Hz */
847d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
848d82bf20eSFrançois Tigeot 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
8499edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8509edbd4a0SFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
851d82bf20eSFrançois Tigeot 	/* 33 - 1920x1080@25Hz */
852d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
853d82bf20eSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
8549edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8559edbd4a0SFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
856d82bf20eSFrançois Tigeot 	/* 34 - 1920x1080@30Hz */
857d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
858d82bf20eSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
8599edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8609edbd4a0SFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
861d82bf20eSFrançois Tigeot 	/* 35 - 2880x480@60Hz */
862d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
863d82bf20eSFrançois Tigeot 		   3192, 3432, 0, 480, 489, 495, 525, 0,
8649edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8659edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
866d82bf20eSFrançois Tigeot 	/* 36 - 2880x480@60Hz */
867d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
868d82bf20eSFrançois Tigeot 		   3192, 3432, 0, 480, 489, 495, 525, 0,
8699edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8709edbd4a0SFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
871d82bf20eSFrançois Tigeot 	/* 37 - 2880x576@50Hz */
872d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
873d82bf20eSFrançois Tigeot 		   3184, 3456, 0, 576, 581, 586, 625, 0,
8749edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8759edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
876d82bf20eSFrançois Tigeot 	/* 38 - 2880x576@50Hz */
877d82bf20eSFrançois Tigeot 	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
878d82bf20eSFrançois Tigeot 		   3184, 3456, 0, 576, 581, 586, 625, 0,
8799edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8809edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
881d82bf20eSFrançois Tigeot 	/* 39 - 1920x1080i@50Hz */
882d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
883d82bf20eSFrançois Tigeot 		   2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
884d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
8859edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
8869edbd4a0SFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
887d82bf20eSFrançois Tigeot 	/* 40 - 1920x1080i@100Hz */
888d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
889d82bf20eSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
890d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
8919edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
8929edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
893d82bf20eSFrançois Tigeot 	/* 41 - 1280x720@100Hz */
894d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
895d82bf20eSFrançois Tigeot 		   1760, 1980, 0, 720, 725, 730, 750, 0,
8969edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8979edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
898d82bf20eSFrançois Tigeot 	/* 42 - 720x576@100Hz */
899d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
900d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
9019edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9029edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
903d82bf20eSFrançois Tigeot 	/* 43 - 720x576@100Hz */
904d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
905d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
9069edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9079edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
9081b13d190SFrançois Tigeot 	/* 44 - 720(1440)x576i@100Hz */
9091b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
9101b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
911d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9121b13d190SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9139edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
9141b13d190SFrançois Tigeot 	/* 45 - 720(1440)x576i@100Hz */
9151b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
9161b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
917d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9181b13d190SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9199edbd4a0SFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
920d82bf20eSFrançois Tigeot 	/* 46 - 1920x1080i@120Hz */
921d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
922d82bf20eSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
923d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
9249edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE),
9259edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
926d82bf20eSFrançois Tigeot 	/* 47 - 1280x720@120Hz */
927d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
928d82bf20eSFrançois Tigeot 		   1430, 1650, 0, 720, 725, 730, 750, 0,
9299edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9309edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
931d82bf20eSFrançois Tigeot 	/* 48 - 720x480@120Hz */
932d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
933d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
9349edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9359edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
936d82bf20eSFrançois Tigeot 	/* 49 - 720x480@120Hz */
937d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
938d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
9399edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9409edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
9411b13d190SFrançois Tigeot 	/* 50 - 720(1440)x480i@120Hz */
9421b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
9431b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
944d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9459edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9469edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
9471b13d190SFrançois Tigeot 	/* 51 - 720(1440)x480i@120Hz */
9481b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
9491b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
950d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9519edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9529edbd4a0SFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
953d82bf20eSFrançois Tigeot 	/* 52 - 720x576@200Hz */
954d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
955d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
9569edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9579edbd4a0SFrançois Tigeot 	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
958d82bf20eSFrançois Tigeot 	/* 53 - 720x576@200Hz */
959d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
960d82bf20eSFrançois Tigeot 		   796, 864, 0, 576, 581, 586, 625, 0,
9619edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9629edbd4a0SFrançois Tigeot 	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
9631b13d190SFrançois Tigeot 	/* 54 - 720(1440)x576i@200Hz */
9641b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
9651b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
966d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9679edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9689edbd4a0SFrançois Tigeot 	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
9691b13d190SFrançois Tigeot 	/* 55 - 720(1440)x576i@200Hz */
9701b13d190SFrançois Tigeot 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
9711b13d190SFrançois Tigeot 		   795, 864, 0, 576, 580, 586, 625, 0,
972d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9739edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9749edbd4a0SFrançois Tigeot 	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
975d82bf20eSFrançois Tigeot 	/* 56 - 720x480@240Hz */
976d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
977d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
9789edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9799edbd4a0SFrançois Tigeot 	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
980d82bf20eSFrançois Tigeot 	/* 57 - 720x480@240Hz */
981d82bf20eSFrançois Tigeot 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
982d82bf20eSFrançois Tigeot 		   798, 858, 0, 480, 489, 495, 525, 0,
9839edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9849edbd4a0SFrançois Tigeot 	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
9854be47400SFrançois Tigeot 	/* 58 - 720(1440)x480i@240Hz */
9861b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
9871b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
988d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9899edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9909edbd4a0SFrançois Tigeot 	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
9914be47400SFrançois Tigeot 	/* 59 - 720(1440)x480i@240Hz */
9921b13d190SFrançois Tigeot 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
9931b13d190SFrançois Tigeot 		   801, 858, 0, 480, 488, 494, 525, 0,
994d82bf20eSFrançois Tigeot 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9959edbd4a0SFrançois Tigeot 			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9969edbd4a0SFrançois Tigeot 	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
997d82bf20eSFrançois Tigeot 	/* 60 - 1280x720@24Hz */
998d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
999d82bf20eSFrançois Tigeot 		   3080, 3300, 0, 720, 725, 730, 750, 0,
10009edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10019edbd4a0SFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1002d82bf20eSFrançois Tigeot 	/* 61 - 1280x720@25Hz */
1003d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
1004d82bf20eSFrançois Tigeot 		   3740, 3960, 0, 720, 725, 730, 750, 0,
10059edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10069edbd4a0SFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1007d82bf20eSFrançois Tigeot 	/* 62 - 1280x720@30Hz */
1008d82bf20eSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
1009d82bf20eSFrançois Tigeot 		   3080, 3300, 0, 720, 725, 730, 750, 0,
10109edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10119edbd4a0SFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1012d82bf20eSFrançois Tigeot 	/* 63 - 1920x1080@120Hz */
1013d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
1014d82bf20eSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
10159edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10169edbd4a0SFrançois Tigeot 	 .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1017d82bf20eSFrançois Tigeot 	/* 64 - 1920x1080@100Hz */
1018d82bf20eSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
10191dedbd3bSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
10209edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10219edbd4a0SFrançois Tigeot 	 .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1022*3f2dd94aSFrançois Tigeot 	/* 65 - 1280x720@24Hz */
1023*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
1024*3f2dd94aSFrançois Tigeot 		   3080, 3300, 0, 720, 725, 730, 750, 0,
1025*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1026*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1027*3f2dd94aSFrançois Tigeot 	/* 66 - 1280x720@25Hz */
1028*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
1029*3f2dd94aSFrançois Tigeot 		   3740, 3960, 0, 720, 725, 730, 750, 0,
1030*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1031*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1032*3f2dd94aSFrançois Tigeot 	/* 67 - 1280x720@30Hz */
1033*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
1034*3f2dd94aSFrançois Tigeot 		   3080, 3300, 0, 720, 725, 730, 750, 0,
1035*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1036*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1037*3f2dd94aSFrançois Tigeot 	/* 68 - 1280x720@50Hz */
1038*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
1039*3f2dd94aSFrançois Tigeot 		   1760, 1980, 0, 720, 725, 730, 750, 0,
1040*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1041*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1042*3f2dd94aSFrançois Tigeot 	/* 69 - 1280x720@60Hz */
1043*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
1044*3f2dd94aSFrançois Tigeot 		   1430, 1650, 0, 720, 725, 730, 750, 0,
1045*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1046*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1047*3f2dd94aSFrançois Tigeot 	/* 70 - 1280x720@100Hz */
1048*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
1049*3f2dd94aSFrançois Tigeot 		   1760, 1980, 0, 720, 725, 730, 750, 0,
1050*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1051*3f2dd94aSFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1052*3f2dd94aSFrançois Tigeot 	/* 71 - 1280x720@120Hz */
1053*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
1054*3f2dd94aSFrançois Tigeot 		   1430, 1650, 0, 720, 725, 730, 750, 0,
1055*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1056*3f2dd94aSFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1057*3f2dd94aSFrançois Tigeot 	/* 72 - 1920x1080@24Hz */
1058*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
1059*3f2dd94aSFrançois Tigeot 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
1060*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1061*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1062*3f2dd94aSFrançois Tigeot 	/* 73 - 1920x1080@25Hz */
1063*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
1064*3f2dd94aSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
1065*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1066*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1067*3f2dd94aSFrançois Tigeot 	/* 74 - 1920x1080@30Hz */
1068*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
1069*3f2dd94aSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
1070*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1071*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1072*3f2dd94aSFrançois Tigeot 	/* 75 - 1920x1080@50Hz */
1073*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
1074*3f2dd94aSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
1075*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1076*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1077*3f2dd94aSFrançois Tigeot 	/* 76 - 1920x1080@60Hz */
1078*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
1079*3f2dd94aSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
1080*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1081*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1082*3f2dd94aSFrançois Tigeot 	/* 77 - 1920x1080@100Hz */
1083*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
1084*3f2dd94aSFrançois Tigeot 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
1085*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1086*3f2dd94aSFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1087*3f2dd94aSFrançois Tigeot 	/* 78 - 1920x1080@120Hz */
1088*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
1089*3f2dd94aSFrançois Tigeot 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
1090*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1091*3f2dd94aSFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1092*3f2dd94aSFrançois Tigeot 	/* 79 - 1680x720@24Hz */
1093*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
1094*3f2dd94aSFrançois Tigeot 		   3080, 3300, 0, 720, 725, 730, 750, 0,
1095*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1096*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1097*3f2dd94aSFrançois Tigeot 	/* 80 - 1680x720@25Hz */
1098*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
1099*3f2dd94aSFrançois Tigeot 		   2948, 3168, 0, 720, 725, 730, 750, 0,
1100*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1101*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1102*3f2dd94aSFrançois Tigeot 	/* 81 - 1680x720@30Hz */
1103*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
1104*3f2dd94aSFrançois Tigeot 		   2420, 2640, 0, 720, 725, 730, 750, 0,
1105*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1106*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1107*3f2dd94aSFrançois Tigeot 	/* 82 - 1680x720@50Hz */
1108*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
1109*3f2dd94aSFrançois Tigeot 		   1980, 2200, 0, 720, 725, 730, 750, 0,
1110*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1111*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1112*3f2dd94aSFrançois Tigeot 	/* 83 - 1680x720@60Hz */
1113*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
1114*3f2dd94aSFrançois Tigeot 		   1980, 2200, 0, 720, 725, 730, 750, 0,
1115*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1116*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1117*3f2dd94aSFrançois Tigeot 	/* 84 - 1680x720@100Hz */
1118*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
1119*3f2dd94aSFrançois Tigeot 		   1780, 2000, 0, 720, 725, 730, 825, 0,
1120*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1121*3f2dd94aSFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1122*3f2dd94aSFrançois Tigeot 	/* 85 - 1680x720@120Hz */
1123*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
1124*3f2dd94aSFrançois Tigeot 		   1780, 2000, 0, 720, 725, 730, 825, 0,
1125*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1126*3f2dd94aSFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1127*3f2dd94aSFrançois Tigeot 	/* 86 - 2560x1080@24Hz */
1128*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
1129*3f2dd94aSFrançois Tigeot 		   3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
1130*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1131*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1132*3f2dd94aSFrançois Tigeot 	/* 87 - 2560x1080@25Hz */
1133*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
1134*3f2dd94aSFrançois Tigeot 		   3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
1135*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1136*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1137*3f2dd94aSFrançois Tigeot 	/* 88 - 2560x1080@30Hz */
1138*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
1139*3f2dd94aSFrançois Tigeot 		   3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
1140*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1141*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1142*3f2dd94aSFrançois Tigeot 	/* 89 - 2560x1080@50Hz */
1143*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
1144*3f2dd94aSFrançois Tigeot 		   3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
1145*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1146*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1147*3f2dd94aSFrançois Tigeot 	/* 90 - 2560x1080@60Hz */
1148*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
1149*3f2dd94aSFrançois Tigeot 		   2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
1150*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1151*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1152*3f2dd94aSFrançois Tigeot 	/* 91 - 2560x1080@100Hz */
1153*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
1154*3f2dd94aSFrançois Tigeot 		   2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
1155*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1156*3f2dd94aSFrançois Tigeot 	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1157*3f2dd94aSFrançois Tigeot 	/* 92 - 2560x1080@120Hz */
1158*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
1159*3f2dd94aSFrançois Tigeot 		   3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
1160*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1161*3f2dd94aSFrançois Tigeot 	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1162*3f2dd94aSFrançois Tigeot 	/* 93 - 3840x2160p@24Hz 16:9 */
1163*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
1164*3f2dd94aSFrançois Tigeot 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1165*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1166*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1167*3f2dd94aSFrançois Tigeot 	/* 94 - 3840x2160p@25Hz 16:9 */
1168*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
1169*3f2dd94aSFrançois Tigeot 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1170*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1171*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1172*3f2dd94aSFrançois Tigeot 	/* 95 - 3840x2160p@30Hz 16:9 */
1173*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
1174*3f2dd94aSFrançois Tigeot 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1175*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1176*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1177*3f2dd94aSFrançois Tigeot 	/* 96 - 3840x2160p@50Hz 16:9 */
1178*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
1179*3f2dd94aSFrançois Tigeot 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1180*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1181*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1182*3f2dd94aSFrançois Tigeot 	/* 97 - 3840x2160p@60Hz 16:9 */
1183*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
1184*3f2dd94aSFrançois Tigeot 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1185*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1186*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1187*3f2dd94aSFrançois Tigeot 	/* 98 - 4096x2160p@24Hz 256:135 */
1188*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
1189*3f2dd94aSFrançois Tigeot 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1190*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1191*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1192*3f2dd94aSFrançois Tigeot 	/* 99 - 4096x2160p@25Hz 256:135 */
1193*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
1194*3f2dd94aSFrançois Tigeot 		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
1195*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1196*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1197*3f2dd94aSFrançois Tigeot 	/* 100 - 4096x2160p@30Hz 256:135 */
1198*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
1199*3f2dd94aSFrançois Tigeot 		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
1200*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1201*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1202*3f2dd94aSFrançois Tigeot 	/* 101 - 4096x2160p@50Hz 256:135 */
1203*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
1204*3f2dd94aSFrançois Tigeot 		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
1205*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1206*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1207*3f2dd94aSFrançois Tigeot 	/* 102 - 4096x2160p@60Hz 256:135 */
1208*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
1209*3f2dd94aSFrançois Tigeot 		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
1210*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1211*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1212*3f2dd94aSFrançois Tigeot 	/* 103 - 3840x2160p@24Hz 64:27 */
1213*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
1214*3f2dd94aSFrançois Tigeot 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1215*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1216*3f2dd94aSFrançois Tigeot 	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1217*3f2dd94aSFrançois Tigeot 	/* 104 - 3840x2160p@25Hz 64:27 */
1218*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
1219*3f2dd94aSFrançois Tigeot 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1220*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1221*3f2dd94aSFrançois Tigeot 	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1222*3f2dd94aSFrançois Tigeot 	/* 105 - 3840x2160p@30Hz 64:27 */
1223*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
1224*3f2dd94aSFrançois Tigeot 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1225*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1226*3f2dd94aSFrançois Tigeot 	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1227*3f2dd94aSFrançois Tigeot 	/* 106 - 3840x2160p@50Hz 64:27 */
1228*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
1229*3f2dd94aSFrançois Tigeot 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1230*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1231*3f2dd94aSFrançois Tigeot 	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1232*3f2dd94aSFrançois Tigeot 	/* 107 - 3840x2160p@60Hz 64:27 */
1233*3f2dd94aSFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
1234*3f2dd94aSFrançois Tigeot 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1235*3f2dd94aSFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
1236*3f2dd94aSFrançois Tigeot 	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
12379edbd4a0SFrançois Tigeot };
12389edbd4a0SFrançois Tigeot 
12399edbd4a0SFrançois Tigeot /*
1240aee94f86SFrançois Tigeot  * HDMI 1.4 4k modes. Index using the VIC.
12419edbd4a0SFrançois Tigeot  */
12429edbd4a0SFrançois Tigeot static const struct drm_display_mode edid_4k_modes[] = {
1243aee94f86SFrançois Tigeot 	/* 0 - dummy, VICs start at 1 */
1244aee94f86SFrançois Tigeot 	{ },
12459edbd4a0SFrançois Tigeot 	/* 1 - 3840x2160@30Hz */
12469edbd4a0SFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
12479edbd4a0SFrançois Tigeot 		   3840, 4016, 4104, 4400, 0,
12489edbd4a0SFrançois Tigeot 		   2160, 2168, 2178, 2250, 0,
12499edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12509edbd4a0SFrançois Tigeot 	  .vrefresh = 30, },
12519edbd4a0SFrançois Tigeot 	/* 2 - 3840x2160@25Hz */
12529edbd4a0SFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
12539edbd4a0SFrançois Tigeot 		   3840, 4896, 4984, 5280, 0,
12549edbd4a0SFrançois Tigeot 		   2160, 2168, 2178, 2250, 0,
12559edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12569edbd4a0SFrançois Tigeot 	  .vrefresh = 25, },
12579edbd4a0SFrançois Tigeot 	/* 3 - 3840x2160@24Hz */
12589edbd4a0SFrançois Tigeot 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
12599edbd4a0SFrançois Tigeot 		   3840, 5116, 5204, 5500, 0,
12609edbd4a0SFrançois Tigeot 		   2160, 2168, 2178, 2250, 0,
12619edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12629edbd4a0SFrançois Tigeot 	  .vrefresh = 24, },
12639edbd4a0SFrançois Tigeot 	/* 4 - 4096x2160@24Hz (SMPTE) */
12649edbd4a0SFrançois Tigeot 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000,
12659edbd4a0SFrançois Tigeot 		   4096, 5116, 5204, 5500, 0,
12669edbd4a0SFrançois Tigeot 		   2160, 2168, 2178, 2250, 0,
12679edbd4a0SFrançois Tigeot 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12689edbd4a0SFrançois Tigeot 	  .vrefresh = 24, },
1269d82bf20eSFrançois Tigeot };
1270d82bf20eSFrançois Tigeot 
12715718399fSFrançois Tigeot /*** DDC fetch and block validation ***/
12725718399fSFrançois Tigeot 
12735718399fSFrançois Tigeot static const u8 edid_header[] = {
12745718399fSFrançois Tigeot 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
12755718399fSFrançois Tigeot };
12765718399fSFrançois Tigeot 
1277ba55f2f5SFrançois Tigeot /**
1278ba55f2f5SFrançois Tigeot  * drm_edid_header_is_valid - sanity check the header of the base EDID block
1279ba55f2f5SFrançois Tigeot  * @raw_edid: pointer to raw base EDID block
1280ba55f2f5SFrançois Tigeot  *
1281ba55f2f5SFrançois Tigeot  * Sanity check the header of the base EDID block.
1282ba55f2f5SFrançois Tigeot  *
1283ba55f2f5SFrançois Tigeot  * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
12845718399fSFrançois Tigeot  */
drm_edid_header_is_valid(const u8 * raw_edid)12855718399fSFrançois Tigeot int drm_edid_header_is_valid(const u8 *raw_edid)
12865718399fSFrançois Tigeot {
12875718399fSFrançois Tigeot 	int i, score = 0;
12885718399fSFrançois Tigeot 
12895718399fSFrançois Tigeot 	for (i = 0; i < sizeof(edid_header); i++)
12905718399fSFrançois Tigeot 		if (raw_edid[i] == edid_header[i])
12915718399fSFrançois Tigeot 			score++;
12925718399fSFrançois Tigeot 
12935718399fSFrançois Tigeot 	return score;
12945718399fSFrançois Tigeot }
1295ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_header_is_valid);
1296ce3d36d7SFrançois Tigeot 
1297ce3d36d7SFrançois Tigeot static int edid_fixup __read_mostly = 6;
1298d82bf20eSFrançois Tigeot module_param_named(edid_fixup, edid_fixup, int, 0400);
1299d82bf20eSFrançois Tigeot MODULE_PARM_DESC(edid_fixup,
1300d82bf20eSFrançois Tigeot 		 "Minimum number of valid EDID header bytes (0-8, default 6)");
13015718399fSFrançois Tigeot 
13028621f407SFrançois Tigeot static void drm_get_displayid(struct drm_connector *connector,
13038621f407SFrançois Tigeot 			      struct edid *edid);
13048621f407SFrançois Tigeot 
drm_edid_block_checksum(const u8 * raw_edid)13052c9916cdSFrançois Tigeot static int drm_edid_block_checksum(const u8 *raw_edid)
13062c9916cdSFrançois Tigeot {
13072c9916cdSFrançois Tigeot 	int i;
13082c9916cdSFrançois Tigeot 	u8 csum = 0;
13092c9916cdSFrançois Tigeot 	for (i = 0; i < EDID_LENGTH; i++)
13102c9916cdSFrançois Tigeot 		csum += raw_edid[i];
13112c9916cdSFrançois Tigeot 
13122c9916cdSFrançois Tigeot 	return csum;
13132c9916cdSFrançois Tigeot }
13142c9916cdSFrançois Tigeot 
drm_edid_is_zero(const u8 * in_edid,int length)13152c9916cdSFrançois Tigeot static bool drm_edid_is_zero(const u8 *in_edid, int length)
13162c9916cdSFrançois Tigeot {
13172c9916cdSFrançois Tigeot 	if (memchr_inv(in_edid, 0, length))
13182c9916cdSFrançois Tigeot 		return false;
13192c9916cdSFrançois Tigeot 
13202c9916cdSFrançois Tigeot 	return true;
13212c9916cdSFrançois Tigeot }
13222c9916cdSFrançois Tigeot 
1323ba55f2f5SFrançois Tigeot /**
1324ba55f2f5SFrançois Tigeot  * drm_edid_block_valid - Sanity check the EDID block (base or extension)
1325ba55f2f5SFrançois Tigeot  * @raw_edid: pointer to raw EDID block
1326ba55f2f5SFrançois Tigeot  * @block: type of block to validate (0 for base, extension otherwise)
1327ba55f2f5SFrançois Tigeot  * @print_bad_edid: if true, dump bad EDID blocks to the console
132819c468b4SFrançois Tigeot  * @edid_corrupt: if true, the header or checksum is invalid
1329ba55f2f5SFrançois Tigeot  *
1330ba55f2f5SFrançois Tigeot  * Validate a base or extension EDID block and optionally dump bad blocks to
1331ba55f2f5SFrançois Tigeot  * the console.
1332ba55f2f5SFrançois Tigeot  *
1333ba55f2f5SFrançois Tigeot  * Return: True if the block is valid, false otherwise.
13345718399fSFrançois Tigeot  */
drm_edid_block_valid(u8 * raw_edid,int block,bool print_bad_edid,bool * edid_corrupt)133519c468b4SFrançois Tigeot bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
133619c468b4SFrançois Tigeot 			  bool *edid_corrupt)
13375718399fSFrançois Tigeot {
13382c9916cdSFrançois Tigeot 	u8 csum;
13395718399fSFrançois Tigeot 	struct edid *edid = (struct edid *)raw_edid;
13405718399fSFrançois Tigeot 
13419edbd4a0SFrançois Tigeot 	if (WARN_ON(!raw_edid))
13429edbd4a0SFrançois Tigeot 		return false;
13439edbd4a0SFrançois Tigeot 
1344ce3d36d7SFrançois Tigeot 	if (edid_fixup > 8 || edid_fixup < 0)
1345ce3d36d7SFrançois Tigeot 		edid_fixup = 6;
1346ce3d36d7SFrançois Tigeot 
1347ce3d36d7SFrançois Tigeot 	if (block == 0) {
13485718399fSFrançois Tigeot 		int score = drm_edid_header_is_valid(raw_edid);
134919c468b4SFrançois Tigeot 		if (score == 8) {
135019c468b4SFrançois Tigeot 			if (edid_corrupt)
135119c468b4SFrançois Tigeot 				*edid_corrupt = false;
135219c468b4SFrançois Tigeot 		} else if (score >= edid_fixup) {
135319c468b4SFrançois Tigeot 			/* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6
135419c468b4SFrançois Tigeot 			 * The corrupt flag needs to be set here otherwise, the
135519c468b4SFrançois Tigeot 			 * fix-up code here will correct the problem, the
135619c468b4SFrançois Tigeot 			 * checksum is correct and the test fails
135719c468b4SFrançois Tigeot 			 */
135819c468b4SFrançois Tigeot 			if (edid_corrupt)
135919c468b4SFrançois Tigeot 				*edid_corrupt = true;
13605718399fSFrançois Tigeot 			DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
13615718399fSFrançois Tigeot 			memcpy(raw_edid, edid_header, sizeof(edid_header));
13625718399fSFrançois Tigeot 		} else {
136319c468b4SFrançois Tigeot 			if (edid_corrupt)
136419c468b4SFrançois Tigeot 				*edid_corrupt = true;
13655718399fSFrançois Tigeot 			goto bad;
13665718399fSFrançois Tigeot 		}
13675718399fSFrançois Tigeot 	}
13685718399fSFrançois Tigeot 
13692c9916cdSFrançois Tigeot 	csum = drm_edid_block_checksum(raw_edid);
13705718399fSFrançois Tigeot 	if (csum) {
137119c468b4SFrançois Tigeot 		if (edid_corrupt)
137219c468b4SFrançois Tigeot 			*edid_corrupt = true;
137319c468b4SFrançois Tigeot 
13745718399fSFrançois Tigeot 		/* allow CEA to slide through, switches mangle this */
1375a85cb24fSFrançois Tigeot 		if (raw_edid[0] == CEA_EXT) {
1376a85cb24fSFrançois Tigeot 			DRM_DEBUG("EDID checksum is invalid, remainder is %d\n", csum);
1377a85cb24fSFrançois Tigeot 			DRM_DEBUG("Assuming a KVM switch modified the CEA block but left the original checksum\n");
1378a85cb24fSFrançois Tigeot 		} else {
1379a85cb24fSFrançois Tigeot 			if (print_bad_edid)
1380a85cb24fSFrançois Tigeot 				DRM_NOTE("EDID checksum is invalid, remainder is %d\n", csum);
1381a85cb24fSFrançois Tigeot 
13825718399fSFrançois Tigeot 			goto bad;
13835718399fSFrançois Tigeot 		}
1384a85cb24fSFrançois Tigeot 	}
13855718399fSFrançois Tigeot 
13865718399fSFrançois Tigeot 	/* per-block-type checks */
13875718399fSFrançois Tigeot 	switch (raw_edid[0]) {
13885718399fSFrançois Tigeot 	case 0: /* base */
13895718399fSFrançois Tigeot 		if (edid->version != 1) {
1390a85cb24fSFrançois Tigeot 			DRM_NOTE("EDID has major version %d, instead of 1\n", edid->version);
13915718399fSFrançois Tigeot 			goto bad;
13925718399fSFrançois Tigeot 		}
13935718399fSFrançois Tigeot 
13945718399fSFrançois Tigeot 		if (edid->revision > 4)
13955718399fSFrançois Tigeot 			DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
13965718399fSFrançois Tigeot 		break;
13975718399fSFrançois Tigeot 
13985718399fSFrançois Tigeot 	default:
13995718399fSFrançois Tigeot 		break;
14005718399fSFrançois Tigeot 	}
14015718399fSFrançois Tigeot 
14029edbd4a0SFrançois Tigeot 	return true;
14035718399fSFrançois Tigeot 
14045718399fSFrançois Tigeot bad:
14059edbd4a0SFrançois Tigeot 	if (print_bad_edid) {
14062c9916cdSFrançois Tigeot 		if (drm_edid_is_zero(raw_edid, EDID_LENGTH)) {
1407a85cb24fSFrançois Tigeot 			pr_notice("EDID block is all zeroes\n");
14082c9916cdSFrançois Tigeot 		} else {
1409a85cb24fSFrançois Tigeot 			pr_notice("Raw EDID:\n");
1410a85cb24fSFrançois Tigeot 			print_hex_dump(KERN_NOTICE,
1411a85cb24fSFrançois Tigeot 				       " \t", DUMP_PREFIX_NONE, 16, 1,
1412dd6db6f0SSascha Wildner 				       raw_edid, EDID_LENGTH, false);
14135718399fSFrançois Tigeot 		}
14142c9916cdSFrançois Tigeot 	}
14159edbd4a0SFrançois Tigeot 	return false;
14165718399fSFrançois Tigeot }
1417ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_block_valid);
14185718399fSFrançois Tigeot 
14195718399fSFrançois Tigeot /**
14205718399fSFrançois Tigeot  * drm_edid_is_valid - sanity check EDID data
14215718399fSFrançois Tigeot  * @edid: EDID data
14225718399fSFrançois Tigeot  *
14235718399fSFrançois Tigeot  * Sanity-check an entire EDID record (including extensions)
1424ba55f2f5SFrançois Tigeot  *
1425ba55f2f5SFrançois Tigeot  * Return: True if the EDID data is valid, false otherwise.
14265718399fSFrançois Tigeot  */
drm_edid_is_valid(struct edid * edid)14275718399fSFrançois Tigeot bool drm_edid_is_valid(struct edid *edid)
14285718399fSFrançois Tigeot {
14295718399fSFrançois Tigeot 	int i;
14305718399fSFrançois Tigeot 	u8 *raw = (u8 *)edid;
14315718399fSFrançois Tigeot 
14325718399fSFrançois Tigeot 	if (!edid)
14335718399fSFrançois Tigeot 		return false;
14345718399fSFrançois Tigeot 
14355718399fSFrançois Tigeot 	for (i = 0; i <= edid->extensions; i++)
143619c468b4SFrançois Tigeot 		if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL))
14375718399fSFrançois Tigeot 			return false;
14385718399fSFrançois Tigeot 
14395718399fSFrançois Tigeot 	return true;
14405718399fSFrançois Tigeot }
1441ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_edid_is_valid);
14425718399fSFrançois Tigeot 
14435718399fSFrançois Tigeot #define DDC_SEGMENT_ADDR 0x30
14445718399fSFrançois Tigeot /**
1445ba55f2f5SFrançois Tigeot  * drm_do_probe_ddc_edid() - get EDID information via I2C
14462c9916cdSFrançois Tigeot  * @data: I2C device adapter
1447ba55f2f5SFrançois Tigeot  * @buf: EDID data buffer to be filled
1448ba55f2f5SFrançois Tigeot  * @block: 128 byte EDID block to start fetching from
1449ba55f2f5SFrançois Tigeot  * @len: EDID data buffer length to fetch
14505718399fSFrançois Tigeot  *
1451ba55f2f5SFrançois Tigeot  * Try to fetch EDID information by calling I2C driver functions.
14525718399fSFrançois Tigeot  *
1453ba55f2f5SFrançois Tigeot  * Return: 0 on success or -1 on failure.
14545718399fSFrançois Tigeot  */
14555718399fSFrançois Tigeot static int
drm_do_probe_ddc_edid(void * data,u8 * buf,unsigned int block,size_t len)14562c9916cdSFrançois Tigeot drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
14575718399fSFrançois Tigeot {
14582c9916cdSFrançois Tigeot 	struct i2c_adapter *adapter = data;
14595718399fSFrançois Tigeot 	unsigned char start = block * EDID_LENGTH;
14606f486c69SFrançois Tigeot 	unsigned char segment = block >> 1;
14616f486c69SFrançois Tigeot 	unsigned char xfers = segment ? 3 : 2;
14625718399fSFrançois Tigeot 	int ret, retries = 5;
14635718399fSFrançois Tigeot 
1464ba55f2f5SFrançois Tigeot 	/*
1465ba55f2f5SFrançois Tigeot 	 * The core I2C driver will automatically retry the transfer if the
14665718399fSFrançois Tigeot 	 * adapter reports EAGAIN. However, we find that bit-banging transfers
14675718399fSFrançois Tigeot 	 * are susceptible to errors under a heavily loaded machine and
14685718399fSFrançois Tigeot 	 * generate spurious NAKs and timeouts. Retrying the transfer
14695718399fSFrançois Tigeot 	 * of the individual block a few times seems to overcome this.
14705718399fSFrançois Tigeot 	 */
14715718399fSFrançois Tigeot 	do {
14726e29dde8SFrançois Tigeot 		struct i2c_msg msgs[] = {
14735718399fSFrançois Tigeot 			{
14749f4ca867SFrançois Tigeot 				.addr	= DDC_SEGMENT_ADDR,
14759f4ca867SFrançois Tigeot 				.flags	= 0,
14769f4ca867SFrançois Tigeot 				.len	= 1,
14779f4ca867SFrançois Tigeot 				.buf	= &segment,
14789f4ca867SFrançois Tigeot 			}, {
14799f4ca867SFrançois Tigeot 				.addr	= DDC_ADDR,
14809f4ca867SFrançois Tigeot 				.flags	= 0,
14819f4ca867SFrançois Tigeot 				.len	= 1,
14829f4ca867SFrançois Tigeot 				.buf	= &start,
14839f4ca867SFrançois Tigeot 			}, {
14849f4ca867SFrançois Tigeot 				.addr	= DDC_ADDR,
14859f4ca867SFrançois Tigeot 				.flags	= I2C_M_RD,
14869f4ca867SFrançois Tigeot 				.len	= len,
14879f4ca867SFrançois Tigeot 				.buf	= buf,
14889f4ca867SFrançois Tigeot 			}
14899f4ca867SFrançois Tigeot 		};
14909f4ca867SFrançois Tigeot 
14919f4ca867SFrançois Tigeot 		/*
14929f4ca867SFrançois Tigeot 		 * Avoid sending the segment addr to not upset non-compliant
14939f4ca867SFrançois Tigeot 		 * DDC monitors.
14949f4ca867SFrançois Tigeot 		 */
14959f4ca867SFrançois Tigeot 		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
14969f4ca867SFrançois Tigeot 
14979f4ca867SFrançois Tigeot 		if (ret == -ENXIO) {
14989f4ca867SFrançois Tigeot 			DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
14999f4ca867SFrançois Tigeot 					adapter->name);
15009f4ca867SFrançois Tigeot 			break;
15019f4ca867SFrançois Tigeot 		}
15029f4ca867SFrançois Tigeot 	} while (ret != xfers && --retries);
15039f4ca867SFrançois Tigeot 
15049f4ca867SFrançois Tigeot 	return ret == xfers ? 0 : -1;
15059f4ca867SFrançois Tigeot }
15069f4ca867SFrançois Tigeot 
connector_bad_edid(struct drm_connector * connector,u8 * edid,int num_blocks)15074be47400SFrançois Tigeot static void connector_bad_edid(struct drm_connector *connector,
15084be47400SFrançois Tigeot 			       u8 *edid, int num_blocks)
15094be47400SFrançois Tigeot {
15104be47400SFrançois Tigeot 	int i;
15114be47400SFrançois Tigeot 
15124be47400SFrançois Tigeot 	if (connector->bad_edid_counter++ && !(drm_debug & DRM_UT_KMS))
15134be47400SFrançois Tigeot 		return;
15144be47400SFrançois Tigeot 
15154be47400SFrançois Tigeot 	dev_warn(connector->dev->dev,
15164be47400SFrançois Tigeot 		 "%s: EDID is invalid:\n",
15174be47400SFrançois Tigeot 		 connector->name);
15184be47400SFrançois Tigeot 	for (i = 0; i < num_blocks; i++) {
15194be47400SFrançois Tigeot 		u8 *block = edid + i * EDID_LENGTH;
15204be47400SFrançois Tigeot 		char prefix[20];
15214be47400SFrançois Tigeot 
15224be47400SFrançois Tigeot 		if (drm_edid_is_zero(block, EDID_LENGTH))
15234be47400SFrançois Tigeot 			ksprintf(prefix, "\t[%02x] ZERO ", i);
15244be47400SFrançois Tigeot 		else if (!drm_edid_block_valid(block, i, false, NULL))
15254be47400SFrançois Tigeot 			ksprintf(prefix, "\t[%02x] BAD  ", i);
15264be47400SFrançois Tigeot 		else
15274be47400SFrançois Tigeot 			ksprintf(prefix, "\t[%02x] GOOD ", i);
15284be47400SFrançois Tigeot 
15294be47400SFrançois Tigeot 		print_hex_dump(KERN_WARNING,
15304be47400SFrançois Tigeot 			       prefix, DUMP_PREFIX_NONE, 16, 1,
15314be47400SFrançois Tigeot 			       block, EDID_LENGTH, false);
15324be47400SFrançois Tigeot 	}
15334be47400SFrançois Tigeot }
15344be47400SFrançois Tigeot 
15352c9916cdSFrançois Tigeot /**
15362c9916cdSFrançois Tigeot  * drm_do_get_edid - get EDID data using a custom EDID block read function
15372c9916cdSFrançois Tigeot  * @connector: connector we're probing
15382c9916cdSFrançois Tigeot  * @get_edid_block: EDID block read function
15392c9916cdSFrançois Tigeot  * @data: private data passed to the block read function
15402c9916cdSFrançois Tigeot  *
15412c9916cdSFrançois Tigeot  * When the I2C adapter connected to the DDC bus is hidden behind a device that
15422c9916cdSFrançois Tigeot  * exposes a different interface to read EDID blocks this function can be used
15432c9916cdSFrançois Tigeot  * to get EDID data using a custom block read function.
15442c9916cdSFrançois Tigeot  *
15452c9916cdSFrançois Tigeot  * As in the general case the DDC bus is accessible by the kernel at the I2C
15462c9916cdSFrançois Tigeot  * level, drivers must make all reasonable efforts to expose it as an I2C
15472c9916cdSFrançois Tigeot  * adapter and use drm_get_edid() instead of abusing this function.
15482c9916cdSFrançois Tigeot  *
1549*3f2dd94aSFrançois Tigeot  * The EDID may be overridden using debugfs override_edid or firmare EDID
1550*3f2dd94aSFrançois Tigeot  * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
1551*3f2dd94aSFrançois Tigeot  * order. Having either of them bypasses actual EDID reads.
1552*3f2dd94aSFrançois Tigeot  *
15532c9916cdSFrançois Tigeot  * Return: Pointer to valid EDID or NULL if we couldn't find any.
15542c9916cdSFrançois Tigeot  */
drm_do_get_edid(struct drm_connector * connector,int (* get_edid_block)(void * data,u8 * buf,unsigned int block,size_t len),void * data)15552c9916cdSFrançois Tigeot struct edid *drm_do_get_edid(struct drm_connector *connector,
15562c9916cdSFrançois Tigeot 	int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
15572c9916cdSFrançois Tigeot 			      size_t len),
15582c9916cdSFrançois Tigeot 	void *data)
15595718399fSFrançois Tigeot {
15605718399fSFrançois Tigeot 	int i, j = 0, valid_extensions = 0;
15614be47400SFrançois Tigeot 	u8 *edid, *new;
1562*3f2dd94aSFrançois Tigeot 	struct edid *override = NULL;
1563*3f2dd94aSFrançois Tigeot 
1564*3f2dd94aSFrançois Tigeot 	if (connector->override_edid)
1565*3f2dd94aSFrançois Tigeot 		override = drm_edid_duplicate((const struct edid *)
1566*3f2dd94aSFrançois Tigeot 					      connector->edid_blob_ptr->data);
1567*3f2dd94aSFrançois Tigeot 
1568*3f2dd94aSFrançois Tigeot 	if (!override)
1569*3f2dd94aSFrançois Tigeot 		override = drm_load_edid_firmware(connector);
1570*3f2dd94aSFrançois Tigeot 
1571*3f2dd94aSFrançois Tigeot 	if (!IS_ERR_OR_NULL(override))
1572*3f2dd94aSFrançois Tigeot 		return override;
15735718399fSFrançois Tigeot 
15744be47400SFrançois Tigeot 	if ((edid = kmalloc(EDID_LENGTH, M_DRM, GFP_KERNEL)) == NULL)
15759edbd4a0SFrançois Tigeot 		return NULL;
15765718399fSFrançois Tigeot 
15775718399fSFrançois Tigeot 	/* base block fetch */
15785718399fSFrançois Tigeot 	for (i = 0; i < 4; i++) {
15794be47400SFrançois Tigeot 		if (get_edid_block(data, edid, 0, EDID_LENGTH))
15805718399fSFrançois Tigeot 			goto out;
15814be47400SFrançois Tigeot 		if (drm_edid_block_valid(edid, 0, false,
158219c468b4SFrançois Tigeot 					 &connector->edid_corrupt))
15835718399fSFrançois Tigeot 			break;
15844be47400SFrançois Tigeot 		if (i == 0 && drm_edid_is_zero(edid, EDID_LENGTH)) {
15855718399fSFrançois Tigeot 			connector->null_edid_counter++;
15865718399fSFrançois Tigeot 			goto carp;
15875718399fSFrançois Tigeot 		}
15885718399fSFrançois Tigeot 	}
15895718399fSFrançois Tigeot 	if (i == 4)
15905718399fSFrançois Tigeot 		goto carp;
15915718399fSFrançois Tigeot 
15925718399fSFrançois Tigeot 	/* if there's no extensions, we're done */
15934be47400SFrançois Tigeot 	valid_extensions = edid[0x7e];
15944be47400SFrançois Tigeot 	if (valid_extensions == 0)
15954be47400SFrançois Tigeot 		return (struct edid *)edid;
15965718399fSFrançois Tigeot 
15974be47400SFrançois Tigeot 	new = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, M_DRM,
15981e12ee3bSFrançois Tigeot 		       GFP_KERNEL);
1599ce3d36d7SFrançois Tigeot 	if (!new)
1600ce3d36d7SFrançois Tigeot 		goto out;
16014be47400SFrançois Tigeot 	edid = new;
16025718399fSFrançois Tigeot 
16034be47400SFrançois Tigeot 	for (j = 1; j <= edid[0x7e]; j++) {
16044be47400SFrançois Tigeot 		u8 *block = edid + j * EDID_LENGTH;
16054be47400SFrançois Tigeot 
16065718399fSFrançois Tigeot 		for (i = 0; i < 4; i++) {
16074be47400SFrançois Tigeot 			if (get_edid_block(data, block, j, EDID_LENGTH))
16085718399fSFrançois Tigeot 				goto out;
16094be47400SFrançois Tigeot 			if (drm_edid_block_valid(block, j, false, NULL))
16105718399fSFrançois Tigeot 				break;
16115718399fSFrançois Tigeot 		}
16124be47400SFrançois Tigeot 
16134be47400SFrançois Tigeot 		if (i == 4)
16144be47400SFrançois Tigeot 			valid_extensions--;
16155718399fSFrançois Tigeot 	}
1616d82bf20eSFrançois Tigeot 
16174be47400SFrançois Tigeot 	if (valid_extensions != edid[0x7e]) {
16184be47400SFrançois Tigeot 		u8 *base;
1619d82bf20eSFrançois Tigeot 
16204be47400SFrançois Tigeot 		connector_bad_edid(connector, edid, edid[0x7e] + 1);
16215718399fSFrançois Tigeot 
16224be47400SFrançois Tigeot 		edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
16234be47400SFrançois Tigeot 		edid[0x7e] = valid_extensions;
16244be47400SFrançois Tigeot 
16254be47400SFrançois Tigeot 		new = kmalloc((valid_extensions + 1) * EDID_LENGTH, M_DRM,
16264be47400SFrançois Tigeot 			      GFP_KERNEL);
1627ce3d36d7SFrançois Tigeot 		if (!new)
1628ce3d36d7SFrançois Tigeot 			goto out;
16294be47400SFrançois Tigeot 
16304be47400SFrançois Tigeot 		base = new;
16314be47400SFrançois Tigeot 		for (i = 0; i <= edid[0x7e]; i++) {
16324be47400SFrançois Tigeot 			u8 *block = edid + i * EDID_LENGTH;
16334be47400SFrançois Tigeot 
16344be47400SFrançois Tigeot 			if (!drm_edid_block_valid(block, i, false, NULL))
16354be47400SFrançois Tigeot 				continue;
16364be47400SFrançois Tigeot 
16374be47400SFrançois Tigeot 			memcpy(base, block, EDID_LENGTH);
16384be47400SFrançois Tigeot 			base += EDID_LENGTH;
16395718399fSFrançois Tigeot 		}
16405718399fSFrançois Tigeot 
16414be47400SFrançois Tigeot 		kfree(edid);
16424be47400SFrançois Tigeot 		edid = new;
16434be47400SFrançois Tigeot 	}
16444be47400SFrançois Tigeot 
16454be47400SFrançois Tigeot 	return (struct edid *)edid;
16465718399fSFrançois Tigeot 
16475718399fSFrançois Tigeot carp:
16484be47400SFrançois Tigeot 	connector_bad_edid(connector, edid, 1);
16495718399fSFrançois Tigeot out:
16504be47400SFrançois Tigeot 	kfree(edid);
16515718399fSFrançois Tigeot 	return NULL;
16525718399fSFrançois Tigeot }
16535718399fSFrançois Tigeot 
16545718399fSFrançois Tigeot /**
1655ba55f2f5SFrançois Tigeot  * drm_probe_ddc() - probe DDC presence
1656ba55f2f5SFrançois Tigeot  * @adapter: I2C adapter to probe
16575718399fSFrançois Tigeot  *
1658ba55f2f5SFrançois Tigeot  * Return: True on success, false on failure.
16595718399fSFrançois Tigeot  */
1660ce3d36d7SFrançois Tigeot bool
drm_probe_ddc(struct i2c_adapter * adapter)16615d302545SFrançois Tigeot drm_probe_ddc(struct i2c_adapter *adapter)
16625718399fSFrançois Tigeot {
16635718399fSFrançois Tigeot 	unsigned char out;
16645718399fSFrançois Tigeot 
16655718399fSFrançois Tigeot 	return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0);
16665718399fSFrançois Tigeot }
1667ce3d36d7SFrançois Tigeot EXPORT_SYMBOL(drm_probe_ddc);
16685718399fSFrançois Tigeot 
16695718399fSFrançois Tigeot /**
16705718399fSFrançois Tigeot  * drm_get_edid - get EDID data, if available
16715718399fSFrançois Tigeot  * @connector: connector we're probing
1672ba55f2f5SFrançois Tigeot  * @adapter: I2C adapter to use for DDC
16735718399fSFrançois Tigeot  *
1674ba55f2f5SFrançois Tigeot  * Poke the given I2C channel to grab EDID data if possible.  If found,
16755718399fSFrançois Tigeot  * attach it to the connector.
16765718399fSFrançois Tigeot  *
1677ba55f2f5SFrançois Tigeot  * Return: Pointer to valid EDID or NULL if we couldn't find any.
16785718399fSFrançois Tigeot  */
drm_get_edid(struct drm_connector * connector,struct i2c_adapter * adapter)16795718399fSFrançois Tigeot struct edid *drm_get_edid(struct drm_connector *connector,
16805d302545SFrançois Tigeot 			  struct i2c_adapter *adapter)
16815718399fSFrançois Tigeot {
16828621f407SFrançois Tigeot 	struct edid *edid;
16838621f407SFrançois Tigeot 
1684a85cb24fSFrançois Tigeot 	if (connector->force == DRM_FORCE_OFF)
1685a85cb24fSFrançois Tigeot 		return NULL;
1686a85cb24fSFrançois Tigeot 
1687a85cb24fSFrançois Tigeot 	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
16882c9916cdSFrançois Tigeot 		return NULL;
16895718399fSFrançois Tigeot 
16908621f407SFrançois Tigeot 	edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
16918621f407SFrançois Tigeot 	if (edid)
16928621f407SFrançois Tigeot 		drm_get_displayid(connector, edid);
16938621f407SFrançois Tigeot 	return edid;
16945718399fSFrançois Tigeot }
1695af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid);
16965718399fSFrançois Tigeot 
16979edbd4a0SFrançois Tigeot /**
1698c0e85e96SFrançois Tigeot  * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output
1699c0e85e96SFrançois Tigeot  * @connector: connector we're probing
1700c0e85e96SFrançois Tigeot  * @adapter: I2C adapter to use for DDC
1701c0e85e96SFrançois Tigeot  *
1702c0e85e96SFrançois Tigeot  * Wrapper around drm_get_edid() for laptops with dual GPUs using one set of
1703c0e85e96SFrançois Tigeot  * outputs. The wrapper adds the requisite vga_switcheroo calls to temporarily
1704c0e85e96SFrançois Tigeot  * switch DDC to the GPU which is retrieving EDID.
1705c0e85e96SFrançois Tigeot  *
1706c0e85e96SFrançois Tigeot  * Return: Pointer to valid EDID or %NULL if we couldn't find any.
1707c0e85e96SFrançois Tigeot  */
drm_get_edid_switcheroo(struct drm_connector * connector,struct i2c_adapter * adapter)1708c0e85e96SFrançois Tigeot struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
1709c0e85e96SFrançois Tigeot 				     struct i2c_adapter *adapter)
1710c0e85e96SFrançois Tigeot {
1711c0e85e96SFrançois Tigeot 	struct pci_dev *pdev = connector->dev->pdev;
1712c0e85e96SFrançois Tigeot 	struct edid *edid;
1713c0e85e96SFrançois Tigeot 
1714c0e85e96SFrançois Tigeot 	vga_switcheroo_lock_ddc(pdev);
1715c0e85e96SFrançois Tigeot 	edid = drm_get_edid(connector, adapter);
1716c0e85e96SFrançois Tigeot 	vga_switcheroo_unlock_ddc(pdev);
1717c0e85e96SFrançois Tigeot 
1718c0e85e96SFrançois Tigeot 	return edid;
1719c0e85e96SFrançois Tigeot }
1720c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_get_edid_switcheroo);
1721c0e85e96SFrançois Tigeot 
1722c0e85e96SFrançois Tigeot /**
17239edbd4a0SFrançois Tigeot  * drm_edid_duplicate - duplicate an EDID and the extensions
17249edbd4a0SFrançois Tigeot  * @edid: EDID to duplicate
17259edbd4a0SFrançois Tigeot  *
1726ba55f2f5SFrançois Tigeot  * Return: Pointer to duplicated EDID or NULL on allocation failure.
17279edbd4a0SFrançois Tigeot  */
drm_edid_duplicate(const struct edid * edid)17289edbd4a0SFrançois Tigeot struct edid *drm_edid_duplicate(const struct edid *edid)
17299edbd4a0SFrançois Tigeot {
17309edbd4a0SFrançois Tigeot 	return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
17319edbd4a0SFrançois Tigeot }
17329edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_duplicate);
17339edbd4a0SFrançois Tigeot 
17345718399fSFrançois Tigeot /*** EDID parsing ***/
17355718399fSFrançois Tigeot 
17365718399fSFrançois Tigeot /**
17375718399fSFrançois Tigeot  * edid_vendor - match a string against EDID's obfuscated vendor field
17385718399fSFrançois Tigeot  * @edid: EDID to match
17395718399fSFrançois Tigeot  * @vendor: vendor string
17405718399fSFrançois Tigeot  *
17415718399fSFrançois Tigeot  * Returns true if @vendor is in @edid, false otherwise
17425718399fSFrançois Tigeot  */
edid_vendor(const struct edid * edid,const char * vendor)1743*3f2dd94aSFrançois Tigeot static bool edid_vendor(const struct edid *edid, const char *vendor)
17445718399fSFrançois Tigeot {
17455718399fSFrançois Tigeot 	char edid_vendor[3];
17465718399fSFrançois Tigeot 
17475718399fSFrançois Tigeot 	edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
17485718399fSFrançois Tigeot 	edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
17495718399fSFrançois Tigeot 			  ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
17505718399fSFrançois Tigeot 	edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@';
17515718399fSFrançois Tigeot 
17525718399fSFrançois Tigeot 	return !strncmp(edid_vendor, vendor, 3);
17535718399fSFrançois Tigeot }
17545718399fSFrançois Tigeot 
17555718399fSFrançois Tigeot /**
17565718399fSFrançois Tigeot  * edid_get_quirks - return quirk flags for a given EDID
17575718399fSFrançois Tigeot  * @edid: EDID to process
17585718399fSFrançois Tigeot  *
17595718399fSFrançois Tigeot  * This tells subsequent routines what fixes they need to apply.
17605718399fSFrançois Tigeot  */
edid_get_quirks(const struct edid * edid)1761*3f2dd94aSFrançois Tigeot static u32 edid_get_quirks(const struct edid *edid)
17625718399fSFrançois Tigeot {
17634be47400SFrançois Tigeot 	const struct edid_quirk *quirk;
17645718399fSFrançois Tigeot 	int i;
17655718399fSFrançois Tigeot 
17666e29dde8SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
17675718399fSFrançois Tigeot 		quirk = &edid_quirk_list[i];
17685718399fSFrançois Tigeot 
17695718399fSFrançois Tigeot 		if (edid_vendor(edid, quirk->vendor) &&
17705718399fSFrançois Tigeot 		    (EDID_PRODUCT_ID(edid) == quirk->product_id))
17715718399fSFrançois Tigeot 			return quirk->quirks;
17725718399fSFrançois Tigeot 	}
17735718399fSFrançois Tigeot 
17745718399fSFrançois Tigeot 	return 0;
17755718399fSFrançois Tigeot }
17765718399fSFrançois Tigeot 
17775718399fSFrançois Tigeot #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
17789edbd4a0SFrançois Tigeot #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
17795718399fSFrançois Tigeot 
17805718399fSFrançois Tigeot /**
17815718399fSFrançois Tigeot  * edid_fixup_preferred - set preferred modes based on quirk list
17825718399fSFrançois Tigeot  * @connector: has mode list to fix up
17835718399fSFrançois Tigeot  * @quirks: quirks list
17845718399fSFrançois Tigeot  *
17855718399fSFrançois Tigeot  * Walk the mode list for @connector, clearing the preferred status
17865718399fSFrançois Tigeot  * on existing modes and setting it anew for the right mode ala @quirks.
17875718399fSFrançois Tigeot  */
edid_fixup_preferred(struct drm_connector * connector,u32 quirks)17885718399fSFrançois Tigeot static void edid_fixup_preferred(struct drm_connector *connector,
17895718399fSFrançois Tigeot 				 u32 quirks)
17905718399fSFrançois Tigeot {
17915718399fSFrançois Tigeot 	struct drm_display_mode *t, *cur_mode, *preferred_mode;
17925718399fSFrançois Tigeot 	int target_refresh = 0;
17939edbd4a0SFrançois Tigeot 	int cur_vrefresh, preferred_vrefresh;
17945718399fSFrançois Tigeot 
17955718399fSFrançois Tigeot 	if (list_empty(&connector->probed_modes))
17965718399fSFrançois Tigeot 		return;
17975718399fSFrançois Tigeot 
17985718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_PREFER_LARGE_60)
17995718399fSFrançois Tigeot 		target_refresh = 60;
18005718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_PREFER_LARGE_75)
18015718399fSFrançois Tigeot 		target_refresh = 75;
18025718399fSFrançois Tigeot 
18035718399fSFrançois Tigeot 	preferred_mode = list_first_entry(&connector->probed_modes,
18045718399fSFrançois Tigeot 					  struct drm_display_mode, head);
18055718399fSFrançois Tigeot 
18065718399fSFrançois Tigeot 	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
18075718399fSFrançois Tigeot 		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
18085718399fSFrançois Tigeot 
18095718399fSFrançois Tigeot 		if (cur_mode == preferred_mode)
18105718399fSFrançois Tigeot 			continue;
18115718399fSFrançois Tigeot 
18125718399fSFrançois Tigeot 		/* Largest mode is preferred */
18135718399fSFrançois Tigeot 		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
18145718399fSFrançois Tigeot 			preferred_mode = cur_mode;
18155718399fSFrançois Tigeot 
18169edbd4a0SFrançois Tigeot 		cur_vrefresh = cur_mode->vrefresh ?
18179edbd4a0SFrançois Tigeot 			cur_mode->vrefresh : drm_mode_vrefresh(cur_mode);
18189edbd4a0SFrançois Tigeot 		preferred_vrefresh = preferred_mode->vrefresh ?
18199edbd4a0SFrançois Tigeot 			preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode);
18205718399fSFrançois Tigeot 		/* At a given size, try to get closest to target refresh */
18215718399fSFrançois Tigeot 		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
18229edbd4a0SFrançois Tigeot 		    MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) <
18239edbd4a0SFrançois Tigeot 		    MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {
18245718399fSFrançois Tigeot 			preferred_mode = cur_mode;
18255718399fSFrançois Tigeot 		}
18265718399fSFrançois Tigeot 	}
18275718399fSFrançois Tigeot 
18285718399fSFrançois Tigeot 	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
18295718399fSFrançois Tigeot }
18305718399fSFrançois Tigeot 
1831ce3d36d7SFrançois Tigeot static bool
mode_is_rb(const struct drm_display_mode * mode)1832ce3d36d7SFrançois Tigeot mode_is_rb(const struct drm_display_mode *mode)
1833ce3d36d7SFrançois Tigeot {
1834ce3d36d7SFrançois Tigeot 	return (mode->htotal - mode->hdisplay == 160) &&
1835ce3d36d7SFrançois Tigeot 	       (mode->hsync_end - mode->hdisplay == 80) &&
1836ce3d36d7SFrançois Tigeot 	       (mode->hsync_end - mode->hsync_start == 32) &&
1837ce3d36d7SFrançois Tigeot 	       (mode->vsync_start - mode->vdisplay == 3);
1838ce3d36d7SFrançois Tigeot }
1839ce3d36d7SFrançois Tigeot 
1840af4b81b9SFrançois Tigeot /*
1841af4b81b9SFrançois Tigeot  * drm_mode_find_dmt - Create a copy of a mode if present in DMT
1842af4b81b9SFrançois Tigeot  * @dev: Device to duplicate against
1843af4b81b9SFrançois Tigeot  * @hsize: Mode width
1844af4b81b9SFrançois Tigeot  * @vsize: Mode height
1845af4b81b9SFrançois Tigeot  * @fresh: Mode refresh rate
1846af4b81b9SFrançois Tigeot  * @rb: Mode reduced-blanking-ness
1847af4b81b9SFrançois Tigeot  *
1848af4b81b9SFrançois Tigeot  * Walk the DMT mode list looking for a match for the given parameters.
1849ba55f2f5SFrançois Tigeot  *
1850ba55f2f5SFrançois Tigeot  * Return: A newly allocated copy of the mode, or NULL if not found.
1851af4b81b9SFrançois Tigeot  */
drm_mode_find_dmt(struct drm_device * dev,int hsize,int vsize,int fresh,bool rb)18525718399fSFrançois Tigeot struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
1853ce3d36d7SFrançois Tigeot 					   int hsize, int vsize, int fresh,
1854ce3d36d7SFrançois Tigeot 					   bool rb)
18555718399fSFrançois Tigeot {
18565718399fSFrançois Tigeot 	int i;
18575718399fSFrançois Tigeot 
1858d82bf20eSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
1859ce3d36d7SFrançois Tigeot 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
1860ce3d36d7SFrançois Tigeot 		if (hsize != ptr->hdisplay)
1861ce3d36d7SFrançois Tigeot 			continue;
1862ce3d36d7SFrançois Tigeot 		if (vsize != ptr->vdisplay)
1863ce3d36d7SFrançois Tigeot 			continue;
1864ce3d36d7SFrançois Tigeot 		if (fresh != drm_mode_vrefresh(ptr))
1865ce3d36d7SFrançois Tigeot 			continue;
1866ce3d36d7SFrançois Tigeot 		if (rb != mode_is_rb(ptr))
1867ce3d36d7SFrançois Tigeot 			continue;
1868ce3d36d7SFrançois Tigeot 
1869ce3d36d7SFrançois Tigeot 		return drm_mode_duplicate(dev, ptr);
18705718399fSFrançois Tigeot 	}
1871ce3d36d7SFrançois Tigeot 
1872ce3d36d7SFrançois Tigeot 	return NULL;
18735718399fSFrançois Tigeot }
1874af4b81b9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_find_dmt);
18755718399fSFrançois Tigeot 
18765718399fSFrançois Tigeot typedef void detailed_cb(struct detailed_timing *timing, void *closure);
18775718399fSFrançois Tigeot 
18785718399fSFrançois Tigeot static void
cea_for_each_detailed_block(u8 * ext,detailed_cb * cb,void * closure)18795718399fSFrançois Tigeot cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
18805718399fSFrançois Tigeot {
18815718399fSFrançois Tigeot 	int i, n = 0;
1882e3b244c9SFrançois Tigeot 	u8 d = ext[0x02];
18835718399fSFrançois Tigeot 	u8 *det_base = ext + d;
18845718399fSFrançois Tigeot 
1885e3b244c9SFrançois Tigeot 	n = (127 - d) / 18;
18865718399fSFrançois Tigeot 	for (i = 0; i < n; i++)
18875718399fSFrançois Tigeot 		cb((struct detailed_timing *)(det_base + 18 * i), closure);
18885718399fSFrançois Tigeot }
18895718399fSFrançois Tigeot 
18905718399fSFrançois Tigeot static void
vtb_for_each_detailed_block(u8 * ext,detailed_cb * cb,void * closure)18915718399fSFrançois Tigeot vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
18925718399fSFrançois Tigeot {
18935718399fSFrançois Tigeot 	unsigned int i, n = min((int)ext[0x02], 6);
18945718399fSFrançois Tigeot 	u8 *det_base = ext + 5;
18955718399fSFrançois Tigeot 
18965718399fSFrançois Tigeot 	if (ext[0x01] != 1)
18975718399fSFrançois Tigeot 		return; /* unknown version */
18985718399fSFrançois Tigeot 
18995718399fSFrançois Tigeot 	for (i = 0; i < n; i++)
19005718399fSFrançois Tigeot 		cb((struct detailed_timing *)(det_base + 18 * i), closure);
19015718399fSFrançois Tigeot }
19025718399fSFrançois Tigeot 
19035718399fSFrançois Tigeot static void
drm_for_each_detailed_block(u8 * raw_edid,detailed_cb * cb,void * closure)19045718399fSFrançois Tigeot drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
19055718399fSFrançois Tigeot {
19065718399fSFrançois Tigeot 	int i;
19075718399fSFrançois Tigeot 	struct edid *edid = (struct edid *)raw_edid;
19085718399fSFrançois Tigeot 
19095718399fSFrançois Tigeot 	if (edid == NULL)
19105718399fSFrançois Tigeot 		return;
19115718399fSFrançois Tigeot 
19125718399fSFrançois Tigeot 	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
19135718399fSFrançois Tigeot 		cb(&(edid->detailed_timings[i]), closure);
19145718399fSFrançois Tigeot 
19155718399fSFrançois Tigeot 	for (i = 1; i <= raw_edid[0x7e]; i++) {
19165718399fSFrançois Tigeot 		u8 *ext = raw_edid + (i * EDID_LENGTH);
19175718399fSFrançois Tigeot 		switch (*ext) {
19185718399fSFrançois Tigeot 		case CEA_EXT:
19195718399fSFrançois Tigeot 			cea_for_each_detailed_block(ext, cb, closure);
19205718399fSFrançois Tigeot 			break;
19215718399fSFrançois Tigeot 		case VTB_EXT:
19225718399fSFrançois Tigeot 			vtb_for_each_detailed_block(ext, cb, closure);
19235718399fSFrançois Tigeot 			break;
19245718399fSFrançois Tigeot 		default:
19255718399fSFrançois Tigeot 			break;
19265718399fSFrançois Tigeot 		}
19275718399fSFrançois Tigeot 	}
19285718399fSFrançois Tigeot }
19295718399fSFrançois Tigeot 
19305718399fSFrançois Tigeot static void
is_rb(struct detailed_timing * t,void * data)19315718399fSFrançois Tigeot is_rb(struct detailed_timing *t, void *data)
19325718399fSFrançois Tigeot {
19335718399fSFrançois Tigeot 	u8 *r = (u8 *)t;
19345718399fSFrançois Tigeot 	if (r[3] == EDID_DETAIL_MONITOR_RANGE)
19355718399fSFrançois Tigeot 		if (r[15] & 0x10)
19365718399fSFrançois Tigeot 			*(bool *)data = true;
19375718399fSFrançois Tigeot }
19385718399fSFrançois Tigeot 
19395718399fSFrançois Tigeot /* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
19405718399fSFrançois Tigeot static bool
drm_monitor_supports_rb(struct edid * edid)19415718399fSFrançois Tigeot drm_monitor_supports_rb(struct edid *edid)
19425718399fSFrançois Tigeot {
19435718399fSFrançois Tigeot 	if (edid->revision >= 4) {
19446e29dde8SFrançois Tigeot 		bool ret = false;
19455718399fSFrançois Tigeot 		drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
19465718399fSFrançois Tigeot 		return ret;
19475718399fSFrançois Tigeot 	}
19485718399fSFrançois Tigeot 
19495718399fSFrançois Tigeot 	return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
19505718399fSFrançois Tigeot }
19515718399fSFrançois Tigeot 
19525718399fSFrançois Tigeot static void
find_gtf2(struct detailed_timing * t,void * data)19535718399fSFrançois Tigeot find_gtf2(struct detailed_timing *t, void *data)
19545718399fSFrançois Tigeot {
19555718399fSFrançois Tigeot 	u8 *r = (u8 *)t;
19565718399fSFrançois Tigeot 	if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02)
19575718399fSFrançois Tigeot 		*(u8 **)data = r;
19585718399fSFrançois Tigeot }
19595718399fSFrançois Tigeot 
19605718399fSFrançois Tigeot /* Secondary GTF curve kicks in above some break frequency */
19615718399fSFrançois Tigeot static int
drm_gtf2_hbreak(struct edid * edid)19625718399fSFrançois Tigeot drm_gtf2_hbreak(struct edid *edid)
19635718399fSFrançois Tigeot {
19645718399fSFrançois Tigeot 	u8 *r = NULL;
19655718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
19665718399fSFrançois Tigeot 	return r ? (r[12] * 2) : 0;
19675718399fSFrançois Tigeot }
19685718399fSFrançois Tigeot 
19695718399fSFrançois Tigeot static int
drm_gtf2_2c(struct edid * edid)19705718399fSFrançois Tigeot drm_gtf2_2c(struct edid *edid)
19715718399fSFrançois Tigeot {
19725718399fSFrançois Tigeot 	u8 *r = NULL;
19735718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
19745718399fSFrançois Tigeot 	return r ? r[13] : 0;
19755718399fSFrançois Tigeot }
19765718399fSFrançois Tigeot 
19775718399fSFrançois Tigeot static int
drm_gtf2_m(struct edid * edid)19785718399fSFrançois Tigeot drm_gtf2_m(struct edid *edid)
19795718399fSFrançois Tigeot {
19805718399fSFrançois Tigeot 	u8 *r = NULL;
19815718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
19825718399fSFrançois Tigeot 	return r ? (r[15] << 8) + r[14] : 0;
19835718399fSFrançois Tigeot }
19845718399fSFrançois Tigeot 
19855718399fSFrançois Tigeot static int
drm_gtf2_k(struct edid * edid)19865718399fSFrançois Tigeot drm_gtf2_k(struct edid *edid)
19875718399fSFrançois Tigeot {
19885718399fSFrançois Tigeot 	u8 *r = NULL;
19895718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
19905718399fSFrançois Tigeot 	return r ? r[16] : 0;
19915718399fSFrançois Tigeot }
19925718399fSFrançois Tigeot 
19935718399fSFrançois Tigeot static int
drm_gtf2_2j(struct edid * edid)19945718399fSFrançois Tigeot drm_gtf2_2j(struct edid *edid)
19955718399fSFrançois Tigeot {
19965718399fSFrançois Tigeot 	u8 *r = NULL;
19975718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
19985718399fSFrançois Tigeot 	return r ? r[17] : 0;
19995718399fSFrançois Tigeot }
20005718399fSFrançois Tigeot 
20015718399fSFrançois Tigeot /**
20025718399fSFrançois Tigeot  * standard_timing_level - get std. timing level(CVT/GTF/DMT)
20035718399fSFrançois Tigeot  * @edid: EDID block to scan
20045718399fSFrançois Tigeot  */
standard_timing_level(struct edid * edid)20055718399fSFrançois Tigeot static int standard_timing_level(struct edid *edid)
20065718399fSFrançois Tigeot {
20075718399fSFrançois Tigeot 	if (edid->revision >= 2) {
20085718399fSFrançois Tigeot 		if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
20095718399fSFrançois Tigeot 			return LEVEL_CVT;
20105718399fSFrançois Tigeot 		if (drm_gtf2_hbreak(edid))
20115718399fSFrançois Tigeot 			return LEVEL_GTF2;
20125718399fSFrançois Tigeot 		return LEVEL_GTF;
20135718399fSFrançois Tigeot 	}
20145718399fSFrançois Tigeot 	return LEVEL_DMT;
20155718399fSFrançois Tigeot }
20165718399fSFrançois Tigeot 
20175718399fSFrançois Tigeot /*
20185718399fSFrançois Tigeot  * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
20195718399fSFrançois Tigeot  * monitors fill with ascii space (0x20) instead.
20205718399fSFrançois Tigeot  */
20215718399fSFrançois Tigeot static int
bad_std_timing(u8 a,u8 b)20225718399fSFrançois Tigeot bad_std_timing(u8 a, u8 b)
20235718399fSFrançois Tigeot {
20245718399fSFrançois Tigeot 	return (a == 0x00 && b == 0x00) ||
20255718399fSFrançois Tigeot 	       (a == 0x01 && b == 0x01) ||
20265718399fSFrançois Tigeot 	       (a == 0x20 && b == 0x20);
20275718399fSFrançois Tigeot }
20285718399fSFrançois Tigeot 
20295718399fSFrançois Tigeot /**
20305718399fSFrançois Tigeot  * drm_mode_std - convert standard mode info (width, height, refresh) into mode
2031ba55f2f5SFrançois Tigeot  * @connector: connector of for the EDID block
2032ba55f2f5SFrançois Tigeot  * @edid: EDID block to scan
20335718399fSFrançois Tigeot  * @t: standard timing params
20345718399fSFrançois Tigeot  *
20355718399fSFrançois Tigeot  * Take the standard timing params (in this case width, aspect, and refresh)
20365718399fSFrançois Tigeot  * and convert them into a real mode using CVT/GTF/DMT.
20375718399fSFrançois Tigeot  */
20385718399fSFrançois Tigeot static struct drm_display_mode *
drm_mode_std(struct drm_connector * connector,struct edid * edid,struct std_timing * t)20395718399fSFrançois Tigeot drm_mode_std(struct drm_connector *connector, struct edid *edid,
2040ba55f2f5SFrançois Tigeot 	     struct std_timing *t)
20415718399fSFrançois Tigeot {
20425718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
20435718399fSFrançois Tigeot 	struct drm_display_mode *m, *mode = NULL;
20445718399fSFrançois Tigeot 	int hsize, vsize;
20455718399fSFrançois Tigeot 	int vrefresh_rate;
20465718399fSFrançois Tigeot 	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
20475718399fSFrançois Tigeot 		>> EDID_TIMING_ASPECT_SHIFT;
20485718399fSFrançois Tigeot 	unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
20495718399fSFrançois Tigeot 		>> EDID_TIMING_VFREQ_SHIFT;
20505718399fSFrançois Tigeot 	int timing_level = standard_timing_level(edid);
20515718399fSFrançois Tigeot 
20525718399fSFrançois Tigeot 	if (bad_std_timing(t->hsize, t->vfreq_aspect))
20535718399fSFrançois Tigeot 		return NULL;
20545718399fSFrançois Tigeot 
20555718399fSFrançois Tigeot 	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
20565718399fSFrançois Tigeot 	hsize = t->hsize * 8 + 248;
20575718399fSFrançois Tigeot 	/* vrefresh_rate = vfreq + 60 */
20585718399fSFrançois Tigeot 	vrefresh_rate = vfreq + 60;
20595718399fSFrançois Tigeot 	/* the vdisplay is calculated based on the aspect ratio */
20605718399fSFrançois Tigeot 	if (aspect_ratio == 0) {
2061ba55f2f5SFrançois Tigeot 		if (edid->revision < 3)
20625718399fSFrançois Tigeot 			vsize = hsize;
20635718399fSFrançois Tigeot 		else
20645718399fSFrançois Tigeot 			vsize = (hsize * 10) / 16;
20655718399fSFrançois Tigeot 	} else if (aspect_ratio == 1)
20665718399fSFrançois Tigeot 		vsize = (hsize * 3) / 4;
20675718399fSFrançois Tigeot 	else if (aspect_ratio == 2)
20685718399fSFrançois Tigeot 		vsize = (hsize * 4) / 5;
20695718399fSFrançois Tigeot 	else
20705718399fSFrançois Tigeot 		vsize = (hsize * 9) / 16;
20715718399fSFrançois Tigeot 
20725718399fSFrançois Tigeot 	/* HDTV hack, part 1 */
20735718399fSFrançois Tigeot 	if (vrefresh_rate == 60 &&
20745718399fSFrançois Tigeot 	    ((hsize == 1360 && vsize == 765) ||
20755718399fSFrançois Tigeot 	     (hsize == 1368 && vsize == 769))) {
20765718399fSFrançois Tigeot 		hsize = 1366;
20775718399fSFrançois Tigeot 		vsize = 768;
20785718399fSFrançois Tigeot 	}
20795718399fSFrançois Tigeot 
20805718399fSFrançois Tigeot 	/*
20815718399fSFrançois Tigeot 	 * If this connector already has a mode for this size and refresh
20825718399fSFrançois Tigeot 	 * rate (because it came from detailed or CVT info), use that
20835718399fSFrançois Tigeot 	 * instead.  This way we don't have to guess at interlace or
20845718399fSFrançois Tigeot 	 * reduced blanking.
20855718399fSFrançois Tigeot 	 */
20865718399fSFrançois Tigeot 	list_for_each_entry(m, &connector->probed_modes, head)
20875718399fSFrançois Tigeot 		if (m->hdisplay == hsize && m->vdisplay == vsize &&
20885718399fSFrançois Tigeot 		    drm_mode_vrefresh(m) == vrefresh_rate)
20895718399fSFrançois Tigeot 			return NULL;
20905718399fSFrançois Tigeot 
20915718399fSFrançois Tigeot 	/* HDTV hack, part 2 */
20925718399fSFrançois Tigeot 	if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
20935718399fSFrançois Tigeot 		mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
20945718399fSFrançois Tigeot 				    false);
20955718399fSFrançois Tigeot 		mode->hdisplay = 1366;
20965718399fSFrançois Tigeot 		mode->hsync_start = mode->hsync_start - 1;
20975718399fSFrançois Tigeot 		mode->hsync_end = mode->hsync_end - 1;
20985718399fSFrançois Tigeot 		return mode;
20995718399fSFrançois Tigeot 	}
21005718399fSFrançois Tigeot 
21015718399fSFrançois Tigeot 	/* check whether it can be found in default mode table */
2102ce3d36d7SFrançois Tigeot 	if (drm_monitor_supports_rb(edid)) {
2103ce3d36d7SFrançois Tigeot 		mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
2104ce3d36d7SFrançois Tigeot 					 true);
2105ce3d36d7SFrançois Tigeot 		if (mode)
2106ce3d36d7SFrançois Tigeot 			return mode;
2107ce3d36d7SFrançois Tigeot 	}
2108ce3d36d7SFrançois Tigeot 	mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false);
21095718399fSFrançois Tigeot 	if (mode)
21105718399fSFrançois Tigeot 		return mode;
21115718399fSFrançois Tigeot 
2112ce3d36d7SFrançois Tigeot 	/* okay, generate it */
21135718399fSFrançois Tigeot 	switch (timing_level) {
21145718399fSFrançois Tigeot 	case LEVEL_DMT:
21155718399fSFrançois Tigeot 		break;
21165718399fSFrançois Tigeot 	case LEVEL_GTF:
21175718399fSFrançois Tigeot 		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
21185718399fSFrançois Tigeot 		break;
21195718399fSFrançois Tigeot 	case LEVEL_GTF2:
21205718399fSFrançois Tigeot 		/*
21215718399fSFrançois Tigeot 		 * This is potentially wrong if there's ever a monitor with
21225718399fSFrançois Tigeot 		 * more than one ranges section, each claiming a different
21235718399fSFrançois Tigeot 		 * secondary GTF curve.  Please don't do that.
21245718399fSFrançois Tigeot 		 */
21255718399fSFrançois Tigeot 		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
2126ce3d36d7SFrançois Tigeot 		if (!mode)
2127ce3d36d7SFrançois Tigeot 			return NULL;
21285718399fSFrançois Tigeot 		if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
2129ce3d36d7SFrançois Tigeot 			drm_mode_destroy(dev, mode);
21305718399fSFrançois Tigeot 			mode = drm_gtf_mode_complex(dev, hsize, vsize,
21315718399fSFrançois Tigeot 						    vrefresh_rate, 0, 0,
21325718399fSFrançois Tigeot 						    drm_gtf2_m(edid),
21335718399fSFrançois Tigeot 						    drm_gtf2_2c(edid),
21345718399fSFrançois Tigeot 						    drm_gtf2_k(edid),
21355718399fSFrançois Tigeot 						    drm_gtf2_2j(edid));
21365718399fSFrançois Tigeot 		}
21375718399fSFrançois Tigeot 		break;
21385718399fSFrançois Tigeot 	case LEVEL_CVT:
21395718399fSFrançois Tigeot 		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
21405718399fSFrançois Tigeot 				    false);
21415718399fSFrançois Tigeot 		break;
21425718399fSFrançois Tigeot 	}
21435718399fSFrançois Tigeot 	return mode;
21445718399fSFrançois Tigeot }
21455718399fSFrançois Tigeot 
21465718399fSFrançois Tigeot /*
21475718399fSFrançois Tigeot  * EDID is delightfully ambiguous about how interlaced modes are to be
21485718399fSFrançois Tigeot  * encoded.  Our internal representation is of frame height, but some
21495718399fSFrançois Tigeot  * HDTV detailed timings are encoded as field height.
21505718399fSFrançois Tigeot  *
21515718399fSFrançois Tigeot  * The format list here is from CEA, in frame size.  Technically we
21525718399fSFrançois Tigeot  * should be checking refresh rate too.  Whatever.
21535718399fSFrançois Tigeot  */
21545718399fSFrançois Tigeot static void
drm_mode_do_interlace_quirk(struct drm_display_mode * mode,struct detailed_pixel_timing * pt)21555718399fSFrançois Tigeot drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
21565718399fSFrançois Tigeot 			    struct detailed_pixel_timing *pt)
21575718399fSFrançois Tigeot {
21585718399fSFrançois Tigeot 	int i;
21595718399fSFrançois Tigeot 	static const struct {
21605718399fSFrançois Tigeot 		int w, h;
21615718399fSFrançois Tigeot 	} cea_interlaced[] = {
21625718399fSFrançois Tigeot 		{ 1920, 1080 },
21635718399fSFrançois Tigeot 		{  720,  480 },
21645718399fSFrançois Tigeot 		{ 1440,  480 },
21655718399fSFrançois Tigeot 		{ 2880,  480 },
21665718399fSFrançois Tigeot 		{  720,  576 },
21675718399fSFrançois Tigeot 		{ 1440,  576 },
21685718399fSFrançois Tigeot 		{ 2880,  576 },
21695718399fSFrançois Tigeot 	};
21705718399fSFrançois Tigeot 
21715718399fSFrançois Tigeot 	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
21725718399fSFrançois Tigeot 		return;
21735718399fSFrançois Tigeot 
21746e29dde8SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
21755718399fSFrançois Tigeot 		if ((mode->hdisplay == cea_interlaced[i].w) &&
21765718399fSFrançois Tigeot 		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
21775718399fSFrançois Tigeot 			mode->vdisplay *= 2;
21785718399fSFrançois Tigeot 			mode->vsync_start *= 2;
21795718399fSFrançois Tigeot 			mode->vsync_end *= 2;
21805718399fSFrançois Tigeot 			mode->vtotal *= 2;
21815718399fSFrançois Tigeot 			mode->vtotal |= 1;
21825718399fSFrançois Tigeot 		}
21835718399fSFrançois Tigeot 	}
21845718399fSFrançois Tigeot 
21855718399fSFrançois Tigeot 	mode->flags |= DRM_MODE_FLAG_INTERLACE;
21865718399fSFrançois Tigeot }
21875718399fSFrançois Tigeot 
21885718399fSFrançois Tigeot /**
21895718399fSFrançois Tigeot  * drm_mode_detailed - create a new mode from an EDID detailed timing section
21905718399fSFrançois Tigeot  * @dev: DRM device (needed to create new mode)
21915718399fSFrançois Tigeot  * @edid: EDID block
21925718399fSFrançois Tigeot  * @timing: EDID detailed timing info
21935718399fSFrançois Tigeot  * @quirks: quirks to apply
21945718399fSFrançois Tigeot  *
21955718399fSFrançois Tigeot  * An EDID detailed timing block contains enough info for us to create and
21965718399fSFrançois Tigeot  * return a new struct drm_display_mode.
21975718399fSFrançois Tigeot  */
drm_mode_detailed(struct drm_device * dev,struct edid * edid,struct detailed_timing * timing,u32 quirks)21985718399fSFrançois Tigeot static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
21995718399fSFrançois Tigeot 						  struct edid *edid,
22005718399fSFrançois Tigeot 						  struct detailed_timing *timing,
22015718399fSFrançois Tigeot 						  u32 quirks)
22025718399fSFrançois Tigeot {
22035718399fSFrançois Tigeot 	struct drm_display_mode *mode;
22045718399fSFrançois Tigeot 	struct detailed_pixel_timing *pt = &timing->data.pixel_data;
22055718399fSFrançois Tigeot 	unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
22065718399fSFrançois Tigeot 	unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
22075718399fSFrançois Tigeot 	unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
22085718399fSFrançois Tigeot 	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;
22095718399fSFrançois Tigeot 	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;
22105718399fSFrançois Tigeot 	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
2211d82bf20eSFrançois Tigeot 	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;
22125718399fSFrançois Tigeot 	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
22135718399fSFrançois Tigeot 
22145718399fSFrançois Tigeot 	/* ignore tiny modes */
22155718399fSFrançois Tigeot 	if (hactive < 64 || vactive < 64)
22165718399fSFrançois Tigeot 		return NULL;
22175718399fSFrançois Tigeot 
22185718399fSFrançois Tigeot 	if (pt->misc & DRM_EDID_PT_STEREO) {
22199edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("stereo mode not supported\n");
22205718399fSFrançois Tigeot 		return NULL;
22215718399fSFrançois Tigeot 	}
22225718399fSFrançois Tigeot 	if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
22239edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("composite sync not supported\n");
22245718399fSFrançois Tigeot 	}
22255718399fSFrançois Tigeot 
22265718399fSFrançois Tigeot 	/* it is incorrect if hsync/vsync width is zero */
22275718399fSFrançois Tigeot 	if (!hsync_pulse_width || !vsync_pulse_width) {
22285718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Incorrect Detailed timing. "
22295718399fSFrançois Tigeot 				"Wrong Hsync/Vsync pulse width\n");
22305718399fSFrançois Tigeot 		return NULL;
22315718399fSFrançois Tigeot 	}
22326e29dde8SFrançois Tigeot 
22336e29dde8SFrançois Tigeot 	if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
22346e29dde8SFrançois Tigeot 		mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
22356e29dde8SFrançois Tigeot 		if (!mode)
22366e29dde8SFrançois Tigeot 			return NULL;
22376e29dde8SFrançois Tigeot 
22386e29dde8SFrançois Tigeot 		goto set_size;
22396e29dde8SFrançois Tigeot 	}
22406e29dde8SFrançois Tigeot 
22415718399fSFrançois Tigeot 	mode = drm_mode_create(dev);
22425718399fSFrançois Tigeot 	if (!mode)
22435718399fSFrançois Tigeot 		return NULL;
22445718399fSFrançois Tigeot 
22455718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
22466e29dde8SFrançois Tigeot 		timing->pixel_clock = cpu_to_le16(1088);
22475718399fSFrançois Tigeot 
22486e29dde8SFrançois Tigeot 	mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
22495718399fSFrançois Tigeot 
22505718399fSFrançois Tigeot 	mode->hdisplay = hactive;
22515718399fSFrançois Tigeot 	mode->hsync_start = mode->hdisplay + hsync_offset;
22525718399fSFrançois Tigeot 	mode->hsync_end = mode->hsync_start + hsync_pulse_width;
22535718399fSFrançois Tigeot 	mode->htotal = mode->hdisplay + hblank;
22545718399fSFrançois Tigeot 
22555718399fSFrançois Tigeot 	mode->vdisplay = vactive;
22565718399fSFrançois Tigeot 	mode->vsync_start = mode->vdisplay + vsync_offset;
22575718399fSFrançois Tigeot 	mode->vsync_end = mode->vsync_start + vsync_pulse_width;
22585718399fSFrançois Tigeot 	mode->vtotal = mode->vdisplay + vblank;
22595718399fSFrançois Tigeot 
22605718399fSFrançois Tigeot 	/* Some EDIDs have bogus h/vtotal values */
22615718399fSFrançois Tigeot 	if (mode->hsync_end > mode->htotal)
22625718399fSFrançois Tigeot 		mode->htotal = mode->hsync_end + 1;
22635718399fSFrançois Tigeot 	if (mode->vsync_end > mode->vtotal)
22645718399fSFrançois Tigeot 		mode->vtotal = mode->vsync_end + 1;
22655718399fSFrançois Tigeot 
22665718399fSFrançois Tigeot 	drm_mode_do_interlace_quirk(mode, pt);
22675718399fSFrançois Tigeot 
22685718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
22695718399fSFrançois Tigeot 		pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
22705718399fSFrançois Tigeot 	}
22715718399fSFrançois Tigeot 
22725718399fSFrançois Tigeot 	mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
22735718399fSFrançois Tigeot 		DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
22745718399fSFrançois Tigeot 	mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
22755718399fSFrançois Tigeot 		DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
22765718399fSFrançois Tigeot 
22776e29dde8SFrançois Tigeot set_size:
22785718399fSFrançois Tigeot 	mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
22795718399fSFrançois Tigeot 	mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
22805718399fSFrançois Tigeot 
22815718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
22825718399fSFrançois Tigeot 		mode->width_mm *= 10;
22835718399fSFrançois Tigeot 		mode->height_mm *= 10;
22845718399fSFrançois Tigeot 	}
22855718399fSFrançois Tigeot 
22865718399fSFrançois Tigeot 	if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
22875718399fSFrançois Tigeot 		mode->width_mm = edid->width_cm * 10;
22885718399fSFrançois Tigeot 		mode->height_mm = edid->height_cm * 10;
22895718399fSFrançois Tigeot 	}
22905718399fSFrançois Tigeot 
22916e29dde8SFrançois Tigeot 	mode->type = DRM_MODE_TYPE_DRIVER;
2292d82bf20eSFrançois Tigeot 	mode->vrefresh = drm_mode_vrefresh(mode);
22936e29dde8SFrançois Tigeot 	drm_mode_set_name(mode);
22946e29dde8SFrançois Tigeot 
22955718399fSFrançois Tigeot 	return mode;
22965718399fSFrançois Tigeot }
22975718399fSFrançois Tigeot 
22985718399fSFrançois Tigeot static bool
mode_in_hsync_range(const struct drm_display_mode * mode,struct edid * edid,u8 * t)2299ce3d36d7SFrançois Tigeot mode_in_hsync_range(const struct drm_display_mode *mode,
23005718399fSFrançois Tigeot 		    struct edid *edid, u8 *t)
23015718399fSFrançois Tigeot {
23025718399fSFrançois Tigeot 	int hsync, hmin, hmax;
23035718399fSFrançois Tigeot 
23045718399fSFrançois Tigeot 	hmin = t[7];
23055718399fSFrançois Tigeot 	if (edid->revision >= 4)
23065718399fSFrançois Tigeot 	    hmin += ((t[4] & 0x04) ? 255 : 0);
23075718399fSFrançois Tigeot 	hmax = t[8];
23085718399fSFrançois Tigeot 	if (edid->revision >= 4)
23095718399fSFrançois Tigeot 	    hmax += ((t[4] & 0x08) ? 255 : 0);
23105718399fSFrançois Tigeot 	hsync = drm_mode_hsync(mode);
23115718399fSFrançois Tigeot 
23125718399fSFrançois Tigeot 	return (hsync <= hmax && hsync >= hmin);
23135718399fSFrançois Tigeot }
23145718399fSFrançois Tigeot 
23155718399fSFrançois Tigeot static bool
mode_in_vsync_range(const struct drm_display_mode * mode,struct edid * edid,u8 * t)2316ce3d36d7SFrançois Tigeot mode_in_vsync_range(const struct drm_display_mode *mode,
23175718399fSFrançois Tigeot 		    struct edid *edid, u8 *t)
23185718399fSFrançois Tigeot {
23195718399fSFrançois Tigeot 	int vsync, vmin, vmax;
23205718399fSFrançois Tigeot 
23215718399fSFrançois Tigeot 	vmin = t[5];
23225718399fSFrançois Tigeot 	if (edid->revision >= 4)
23235718399fSFrançois Tigeot 	    vmin += ((t[4] & 0x01) ? 255 : 0);
23245718399fSFrançois Tigeot 	vmax = t[6];
23255718399fSFrançois Tigeot 	if (edid->revision >= 4)
23265718399fSFrançois Tigeot 	    vmax += ((t[4] & 0x02) ? 255 : 0);
23275718399fSFrançois Tigeot 	vsync = drm_mode_vrefresh(mode);
23285718399fSFrançois Tigeot 
23295718399fSFrançois Tigeot 	return (vsync <= vmax && vsync >= vmin);
23305718399fSFrançois Tigeot }
23315718399fSFrançois Tigeot 
23325718399fSFrançois Tigeot static u32
range_pixel_clock(struct edid * edid,u8 * t)23335718399fSFrançois Tigeot range_pixel_clock(struct edid *edid, u8 *t)
23345718399fSFrançois Tigeot {
23355718399fSFrançois Tigeot 	/* unspecified */
23365718399fSFrançois Tigeot 	if (t[9] == 0 || t[9] == 255)
23375718399fSFrançois Tigeot 		return 0;
23385718399fSFrançois Tigeot 
23395718399fSFrançois Tigeot 	/* 1.4 with CVT support gives us real precision, yay */
23405718399fSFrançois Tigeot 	if (edid->revision >= 4 && t[10] == 0x04)
23415718399fSFrançois Tigeot 		return (t[9] * 10000) - ((t[12] >> 2) * 250);
23425718399fSFrançois Tigeot 
23435718399fSFrançois Tigeot 	/* 1.3 is pathetic, so fuzz up a bit */
23445718399fSFrançois Tigeot 	return t[9] * 10000 + 5001;
23455718399fSFrançois Tigeot }
23465718399fSFrançois Tigeot 
23475718399fSFrançois Tigeot static bool
mode_in_range(const struct drm_display_mode * mode,struct edid * edid,struct detailed_timing * timing)2348ce3d36d7SFrançois Tigeot mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
23495718399fSFrançois Tigeot 	      struct detailed_timing *timing)
23505718399fSFrançois Tigeot {
23515718399fSFrançois Tigeot 	u32 max_clock;
23525718399fSFrançois Tigeot 	u8 *t = (u8 *)timing;
23535718399fSFrançois Tigeot 
23545718399fSFrançois Tigeot 	if (!mode_in_hsync_range(mode, edid, t))
23555718399fSFrançois Tigeot 		return false;
23565718399fSFrançois Tigeot 
23575718399fSFrançois Tigeot 	if (!mode_in_vsync_range(mode, edid, t))
23585718399fSFrançois Tigeot 		return false;
23595718399fSFrançois Tigeot 
23605718399fSFrançois Tigeot 	if ((max_clock = range_pixel_clock(edid, t)))
23615718399fSFrançois Tigeot 		if (mode->clock > max_clock)
23625718399fSFrançois Tigeot 			return false;
23635718399fSFrançois Tigeot 
23645718399fSFrançois Tigeot 	/* 1.4 max horizontal check */
23655718399fSFrançois Tigeot 	if (edid->revision >= 4 && t[10] == 0x04)
23665718399fSFrançois Tigeot 		if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
23675718399fSFrançois Tigeot 			return false;
23685718399fSFrançois Tigeot 
23695718399fSFrançois Tigeot 	if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
23705718399fSFrançois Tigeot 		return false;
23715718399fSFrançois Tigeot 
23725718399fSFrançois Tigeot 	return true;
23735718399fSFrançois Tigeot }
23745718399fSFrançois Tigeot 
valid_inferred_mode(const struct drm_connector * connector,const struct drm_display_mode * mode)23756e29dde8SFrançois Tigeot static bool valid_inferred_mode(const struct drm_connector *connector,
23766e29dde8SFrançois Tigeot 				const struct drm_display_mode *mode)
23776e29dde8SFrançois Tigeot {
23781e12ee3bSFrançois Tigeot 	const struct drm_display_mode *m;
23796e29dde8SFrançois Tigeot 	bool ok = false;
23806e29dde8SFrançois Tigeot 
23816e29dde8SFrançois Tigeot 	list_for_each_entry(m, &connector->probed_modes, head) {
23826e29dde8SFrançois Tigeot 		if (mode->hdisplay == m->hdisplay &&
23836e29dde8SFrançois Tigeot 		    mode->vdisplay == m->vdisplay &&
23846e29dde8SFrançois Tigeot 		    drm_mode_vrefresh(mode) == drm_mode_vrefresh(m))
23856e29dde8SFrançois Tigeot 			return false; /* duplicated */
23866e29dde8SFrançois Tigeot 		if (mode->hdisplay <= m->hdisplay &&
23876e29dde8SFrançois Tigeot 		    mode->vdisplay <= m->vdisplay)
23886e29dde8SFrançois Tigeot 			ok = true;
23896e29dde8SFrançois Tigeot 	}
23906e29dde8SFrançois Tigeot 	return ok;
23916e29dde8SFrançois Tigeot }
23926e29dde8SFrançois Tigeot 
23935718399fSFrançois Tigeot static int
drm_dmt_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)23946e29dde8SFrançois Tigeot drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid,
23955718399fSFrançois Tigeot 			struct detailed_timing *timing)
23965718399fSFrançois Tigeot {
23975718399fSFrançois Tigeot 	int i, modes = 0;
23985718399fSFrançois Tigeot 	struct drm_display_mode *newmode;
23995718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
24005718399fSFrançois Tigeot 
2401d82bf20eSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
24026e29dde8SFrançois Tigeot 		if (mode_in_range(drm_dmt_modes + i, edid, timing) &&
24036e29dde8SFrançois Tigeot 		    valid_inferred_mode(connector, drm_dmt_modes + i)) {
24045718399fSFrançois Tigeot 			newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
24055718399fSFrançois Tigeot 			if (newmode) {
24065718399fSFrançois Tigeot 				drm_mode_probed_add(connector, newmode);
24075718399fSFrançois Tigeot 				modes++;
24085718399fSFrançois Tigeot 			}
24095718399fSFrançois Tigeot 		}
24105718399fSFrançois Tigeot 	}
24115718399fSFrançois Tigeot 
24125718399fSFrançois Tigeot 	return modes;
24135718399fSFrançois Tigeot }
24145718399fSFrançois Tigeot 
24156e29dde8SFrançois Tigeot /* fix up 1366x768 mode from 1368x768;
24166e29dde8SFrançois Tigeot  * GFT/CVT can't express 1366 width which isn't dividable by 8
24176e29dde8SFrançois Tigeot  */
drm_mode_fixup_1366x768(struct drm_display_mode * mode)2418a85cb24fSFrançois Tigeot void drm_mode_fixup_1366x768(struct drm_display_mode *mode)
24196e29dde8SFrançois Tigeot {
24206e29dde8SFrançois Tigeot 	if (mode->hdisplay == 1368 && mode->vdisplay == 768) {
24216e29dde8SFrançois Tigeot 		mode->hdisplay = 1366;
24226e29dde8SFrançois Tigeot 		mode->hsync_start--;
24236e29dde8SFrançois Tigeot 		mode->hsync_end--;
24246e29dde8SFrançois Tigeot 		drm_mode_set_name(mode);
24256e29dde8SFrançois Tigeot 	}
24266e29dde8SFrançois Tigeot }
24276e29dde8SFrançois Tigeot 
24286e29dde8SFrançois Tigeot static int
drm_gtf_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)24296e29dde8SFrançois Tigeot drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
24306e29dde8SFrançois Tigeot 			struct detailed_timing *timing)
24316e29dde8SFrançois Tigeot {
24326e29dde8SFrançois Tigeot 	int i, modes = 0;
24336e29dde8SFrançois Tigeot 	struct drm_display_mode *newmode;
24346e29dde8SFrançois Tigeot 	struct drm_device *dev = connector->dev;
24356e29dde8SFrançois Tigeot 
2436d82bf20eSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
24376e29dde8SFrançois Tigeot 		const struct minimode *m = &extra_modes[i];
24386e29dde8SFrançois Tigeot 		newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
24396e29dde8SFrançois Tigeot 		if (!newmode)
24406e29dde8SFrançois Tigeot 			return modes;
24416e29dde8SFrançois Tigeot 
2442a85cb24fSFrançois Tigeot 		drm_mode_fixup_1366x768(newmode);
24436e29dde8SFrançois Tigeot 		if (!mode_in_range(newmode, edid, timing) ||
24446e29dde8SFrançois Tigeot 		    !valid_inferred_mode(connector, newmode)) {
24456e29dde8SFrançois Tigeot 			drm_mode_destroy(dev, newmode);
24466e29dde8SFrançois Tigeot 			continue;
24476e29dde8SFrançois Tigeot 		}
24486e29dde8SFrançois Tigeot 
24496e29dde8SFrançois Tigeot 		drm_mode_probed_add(connector, newmode);
24506e29dde8SFrançois Tigeot 		modes++;
24516e29dde8SFrançois Tigeot 	}
24526e29dde8SFrançois Tigeot 
24536e29dde8SFrançois Tigeot 	return modes;
24546e29dde8SFrançois Tigeot }
24556e29dde8SFrançois Tigeot 
24566e29dde8SFrançois Tigeot static int
drm_cvt_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)24576e29dde8SFrançois Tigeot drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
24586e29dde8SFrançois Tigeot 			struct detailed_timing *timing)
24596e29dde8SFrançois Tigeot {
24606e29dde8SFrançois Tigeot 	int i, modes = 0;
24616e29dde8SFrançois Tigeot 	struct drm_display_mode *newmode;
24626e29dde8SFrançois Tigeot 	struct drm_device *dev = connector->dev;
24636e29dde8SFrançois Tigeot 	bool rb = drm_monitor_supports_rb(edid);
24646e29dde8SFrançois Tigeot 
2465d82bf20eSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
24666e29dde8SFrançois Tigeot 		const struct minimode *m = &extra_modes[i];
24676e29dde8SFrançois Tigeot 		newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
24686e29dde8SFrançois Tigeot 		if (!newmode)
24696e29dde8SFrançois Tigeot 			return modes;
24706e29dde8SFrançois Tigeot 
2471a85cb24fSFrançois Tigeot 		drm_mode_fixup_1366x768(newmode);
24726e29dde8SFrançois Tigeot 		if (!mode_in_range(newmode, edid, timing) ||
24736e29dde8SFrançois Tigeot 		    !valid_inferred_mode(connector, newmode)) {
24746e29dde8SFrançois Tigeot 			drm_mode_destroy(dev, newmode);
24756e29dde8SFrançois Tigeot 			continue;
24766e29dde8SFrançois Tigeot 		}
24776e29dde8SFrançois Tigeot 
24786e29dde8SFrançois Tigeot 		drm_mode_probed_add(connector, newmode);
24796e29dde8SFrançois Tigeot 		modes++;
24806e29dde8SFrançois Tigeot 	}
24816e29dde8SFrançois Tigeot 
24826e29dde8SFrançois Tigeot 	return modes;
24836e29dde8SFrançois Tigeot }
24846e29dde8SFrançois Tigeot 
24855718399fSFrançois Tigeot static void
do_inferred_modes(struct detailed_timing * timing,void * c)24865718399fSFrançois Tigeot do_inferred_modes(struct detailed_timing *timing, void *c)
24875718399fSFrançois Tigeot {
24885718399fSFrançois Tigeot 	struct detailed_mode_closure *closure = c;
24895718399fSFrançois Tigeot 	struct detailed_non_pixel *data = &timing->data.other_data;
24906e29dde8SFrançois Tigeot 	struct detailed_data_monitor_range *range = &data->data.range;
24915718399fSFrançois Tigeot 
24926e29dde8SFrançois Tigeot 	if (data->type != EDID_DETAIL_MONITOR_RANGE)
24936e29dde8SFrançois Tigeot 		return;
24946e29dde8SFrançois Tigeot 
24956e29dde8SFrançois Tigeot 	closure->modes += drm_dmt_modes_for_range(closure->connector,
24966e29dde8SFrançois Tigeot 						  closure->edid,
24976e29dde8SFrançois Tigeot 						  timing);
24986e29dde8SFrançois Tigeot 
24996e29dde8SFrançois Tigeot 	if (!version_greater(closure->edid, 1, 1))
25006e29dde8SFrançois Tigeot 		return; /* GTF not defined yet */
25016e29dde8SFrançois Tigeot 
25026e29dde8SFrançois Tigeot 	switch (range->flags) {
25036e29dde8SFrançois Tigeot 	case 0x02: /* secondary gtf, XXX could do more */
25046e29dde8SFrançois Tigeot 	case 0x00: /* default gtf */
25055718399fSFrançois Tigeot 		closure->modes += drm_gtf_modes_for_range(closure->connector,
25065718399fSFrançois Tigeot 							  closure->edid,
25075718399fSFrançois Tigeot 							  timing);
25086e29dde8SFrançois Tigeot 		break;
25096e29dde8SFrançois Tigeot 	case 0x04: /* cvt, only in 1.4+ */
25106e29dde8SFrançois Tigeot 		if (!version_greater(closure->edid, 1, 3))
25116e29dde8SFrançois Tigeot 			break;
25126e29dde8SFrançois Tigeot 
25136e29dde8SFrançois Tigeot 		closure->modes += drm_cvt_modes_for_range(closure->connector,
25146e29dde8SFrançois Tigeot 							  closure->edid,
25156e29dde8SFrançois Tigeot 							  timing);
25166e29dde8SFrançois Tigeot 		break;
25176e29dde8SFrançois Tigeot 	case 0x01: /* just the ranges, no formula */
25186e29dde8SFrançois Tigeot 	default:
25196e29dde8SFrançois Tigeot 		break;
25206e29dde8SFrançois Tigeot 	}
25215718399fSFrançois Tigeot }
25225718399fSFrançois Tigeot 
25235718399fSFrançois Tigeot static int
add_inferred_modes(struct drm_connector * connector,struct edid * edid)25245718399fSFrançois Tigeot add_inferred_modes(struct drm_connector *connector, struct edid *edid)
25255718399fSFrançois Tigeot {
25265718399fSFrançois Tigeot 	struct detailed_mode_closure closure = {
252755a9fa67Szrj 		.connector = connector,
252855a9fa67Szrj 		.edid = edid,
25295718399fSFrançois Tigeot 	};
25305718399fSFrançois Tigeot 
25315718399fSFrançois Tigeot 	if (version_greater(edid, 1, 0))
25325718399fSFrançois Tigeot 		drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
25335718399fSFrançois Tigeot 					    &closure);
25345718399fSFrançois Tigeot 
25355718399fSFrançois Tigeot 	return closure.modes;
25365718399fSFrançois Tigeot }
25375718399fSFrançois Tigeot 
25385718399fSFrançois Tigeot static int
drm_est3_modes(struct drm_connector * connector,struct detailed_timing * timing)25395718399fSFrançois Tigeot drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
25405718399fSFrançois Tigeot {
25415718399fSFrançois Tigeot 	int i, j, m, modes = 0;
25425718399fSFrançois Tigeot 	struct drm_display_mode *mode;
2543c0e85e96SFrançois Tigeot 	u8 *est = ((u8 *)timing) + 6;
25445718399fSFrançois Tigeot 
25455718399fSFrançois Tigeot 	for (i = 0; i < 6; i++) {
25469edbd4a0SFrançois Tigeot 		for (j = 7; j >= 0; j--) {
25475718399fSFrançois Tigeot 			m = (i * 8) + (7 - j);
2548ce3d36d7SFrançois Tigeot 			if (m >= ARRAY_SIZE(est3_modes))
25495718399fSFrançois Tigeot 				break;
25505718399fSFrançois Tigeot 			if (est[i] & (1 << j)) {
25515718399fSFrançois Tigeot 				mode = drm_mode_find_dmt(connector->dev,
25525718399fSFrançois Tigeot 							 est3_modes[m].w,
25535718399fSFrançois Tigeot 							 est3_modes[m].h,
2554ce3d36d7SFrançois Tigeot 							 est3_modes[m].r,
2555ce3d36d7SFrançois Tigeot 							 est3_modes[m].rb);
25565718399fSFrançois Tigeot 				if (mode) {
25575718399fSFrançois Tigeot 					drm_mode_probed_add(connector, mode);
25585718399fSFrançois Tigeot 					modes++;
25595718399fSFrançois Tigeot 				}
25605718399fSFrançois Tigeot 			}
25615718399fSFrançois Tigeot 		}
25625718399fSFrançois Tigeot 	}
25635718399fSFrançois Tigeot 
25645718399fSFrançois Tigeot 	return modes;
25655718399fSFrançois Tigeot }
25665718399fSFrançois Tigeot 
25675718399fSFrançois Tigeot static void
do_established_modes(struct detailed_timing * timing,void * c)25685718399fSFrançois Tigeot do_established_modes(struct detailed_timing *timing, void *c)
25695718399fSFrançois Tigeot {
25705718399fSFrançois Tigeot 	struct detailed_mode_closure *closure = c;
25715718399fSFrançois Tigeot 	struct detailed_non_pixel *data = &timing->data.other_data;
25725718399fSFrançois Tigeot 
25735718399fSFrançois Tigeot 	if (data->type == EDID_DETAIL_EST_TIMINGS)
25745718399fSFrançois Tigeot 		closure->modes += drm_est3_modes(closure->connector, timing);
25755718399fSFrançois Tigeot }
25765718399fSFrançois Tigeot 
25775718399fSFrançois Tigeot /**
25785718399fSFrançois Tigeot  * add_established_modes - get est. modes from EDID and add them
2579ba55f2f5SFrançois Tigeot  * @connector: connector to add mode(s) to
25805718399fSFrançois Tigeot  * @edid: EDID block to scan
25815718399fSFrançois Tigeot  *
25825718399fSFrançois Tigeot  * Each EDID block contains a bitmap of the supported "established modes" list
25835718399fSFrançois Tigeot  * (defined above).  Tease them out and add them to the global modes list.
25845718399fSFrançois Tigeot  */
25855718399fSFrançois Tigeot static int
add_established_modes(struct drm_connector * connector,struct edid * edid)25865718399fSFrançois Tigeot add_established_modes(struct drm_connector *connector, struct edid *edid)
25875718399fSFrançois Tigeot {
25885718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
25895718399fSFrançois Tigeot 	unsigned long est_bits = edid->established_timings.t1 |
25905718399fSFrançois Tigeot 		(edid->established_timings.t2 << 8) |
25915718399fSFrançois Tigeot 		((edid->established_timings.mfg_rsvd & 0x80) << 9);
25925718399fSFrançois Tigeot 	int i, modes = 0;
25935718399fSFrançois Tigeot 	struct detailed_mode_closure closure = {
259455a9fa67Szrj 		.connector = connector,
259555a9fa67Szrj 		.edid = edid,
25965718399fSFrançois Tigeot 	};
25975718399fSFrançois Tigeot 
25985718399fSFrançois Tigeot 	for (i = 0; i <= EDID_EST_TIMINGS; i++) {
25995718399fSFrançois Tigeot 		if (est_bits & (1<<i)) {
26005718399fSFrançois Tigeot 			struct drm_display_mode *newmode;
26015718399fSFrançois Tigeot 			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
26025718399fSFrançois Tigeot 			if (newmode) {
26035718399fSFrançois Tigeot 				drm_mode_probed_add(connector, newmode);
26045718399fSFrançois Tigeot 				modes++;
26055718399fSFrançois Tigeot 			}
26065718399fSFrançois Tigeot 		}
26075718399fSFrançois Tigeot 	}
26085718399fSFrançois Tigeot 
26095718399fSFrançois Tigeot 	if (version_greater(edid, 1, 0))
26105718399fSFrançois Tigeot 		    drm_for_each_detailed_block((u8 *)edid,
26115718399fSFrançois Tigeot 						do_established_modes, &closure);
26125718399fSFrançois Tigeot 
26135718399fSFrançois Tigeot 	return modes + closure.modes;
26145718399fSFrançois Tigeot }
26155718399fSFrançois Tigeot 
26165718399fSFrançois Tigeot static void
do_standard_modes(struct detailed_timing * timing,void * c)26175718399fSFrançois Tigeot do_standard_modes(struct detailed_timing *timing, void *c)
26185718399fSFrançois Tigeot {
26195718399fSFrançois Tigeot 	struct detailed_mode_closure *closure = c;
26205718399fSFrançois Tigeot 	struct detailed_non_pixel *data = &timing->data.other_data;
26215718399fSFrançois Tigeot 	struct drm_connector *connector = closure->connector;
26225718399fSFrançois Tigeot 	struct edid *edid = closure->edid;
26235718399fSFrançois Tigeot 
26245718399fSFrançois Tigeot 	if (data->type == EDID_DETAIL_STD_MODES) {
26255718399fSFrançois Tigeot 		int i;
26265718399fSFrançois Tigeot 		for (i = 0; i < 6; i++) {
26275718399fSFrançois Tigeot 			struct std_timing *std;
26285718399fSFrançois Tigeot 			struct drm_display_mode *newmode;
26295718399fSFrançois Tigeot 
26305718399fSFrançois Tigeot 			std = &data->data.timings[i];
2631ba55f2f5SFrançois Tigeot 			newmode = drm_mode_std(connector, edid, std);
26325718399fSFrançois Tigeot 			if (newmode) {
26335718399fSFrançois Tigeot 				drm_mode_probed_add(connector, newmode);
26345718399fSFrançois Tigeot 				closure->modes++;
26355718399fSFrançois Tigeot 			}
26365718399fSFrançois Tigeot 		}
26375718399fSFrançois Tigeot 	}
26385718399fSFrançois Tigeot }
26395718399fSFrançois Tigeot 
26405718399fSFrançois Tigeot /**
26415718399fSFrançois Tigeot  * add_standard_modes - get std. modes from EDID and add them
2642ba55f2f5SFrançois Tigeot  * @connector: connector to add mode(s) to
26435718399fSFrançois Tigeot  * @edid: EDID block to scan
26445718399fSFrançois Tigeot  *
26455718399fSFrançois Tigeot  * Standard modes can be calculated using the appropriate standard (DMT,
26465718399fSFrançois Tigeot  * GTF or CVT. Grab them from @edid and add them to the list.
26475718399fSFrançois Tigeot  */
26485718399fSFrançois Tigeot static int
add_standard_modes(struct drm_connector * connector,struct edid * edid)26495718399fSFrançois Tigeot add_standard_modes(struct drm_connector *connector, struct edid *edid)
26505718399fSFrançois Tigeot {
26515718399fSFrançois Tigeot 	int i, modes = 0;
26525718399fSFrançois Tigeot 	struct detailed_mode_closure closure = {
265355a9fa67Szrj 		.connector = connector,
265455a9fa67Szrj 		.edid = edid,
26555718399fSFrançois Tigeot 	};
26565718399fSFrançois Tigeot 
26575718399fSFrançois Tigeot 	for (i = 0; i < EDID_STD_TIMINGS; i++) {
26585718399fSFrançois Tigeot 		struct drm_display_mode *newmode;
26595718399fSFrançois Tigeot 
26605718399fSFrançois Tigeot 		newmode = drm_mode_std(connector, edid,
2661ba55f2f5SFrançois Tigeot 				       &edid->standard_timings[i]);
26625718399fSFrançois Tigeot 		if (newmode) {
26635718399fSFrançois Tigeot 			drm_mode_probed_add(connector, newmode);
26645718399fSFrançois Tigeot 			modes++;
26655718399fSFrançois Tigeot 		}
26665718399fSFrançois Tigeot 	}
26675718399fSFrançois Tigeot 
26685718399fSFrançois Tigeot 	if (version_greater(edid, 1, 0))
26695718399fSFrançois Tigeot 		drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
26705718399fSFrançois Tigeot 					    &closure);
26715718399fSFrançois Tigeot 
26725718399fSFrançois Tigeot 	/* XXX should also look for standard codes in VTB blocks */
26735718399fSFrançois Tigeot 
26745718399fSFrançois Tigeot 	return modes + closure.modes;
26755718399fSFrançois Tigeot }
26765718399fSFrançois Tigeot 
drm_cvt_modes(struct drm_connector * connector,struct detailed_timing * timing)26775718399fSFrançois Tigeot static int drm_cvt_modes(struct drm_connector *connector,
26785718399fSFrançois Tigeot 			 struct detailed_timing *timing)
26795718399fSFrançois Tigeot {
26805718399fSFrançois Tigeot 	int i, j, modes = 0;
26815718399fSFrançois Tigeot 	struct drm_display_mode *newmode;
26825718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
26835718399fSFrançois Tigeot 	struct cvt_timing *cvt;
26845718399fSFrançois Tigeot 	const int rates[] = { 60, 85, 75, 60, 50 };
26855718399fSFrançois Tigeot 	const u8 empty[3] = { 0, 0, 0 };
26865718399fSFrançois Tigeot 
26875718399fSFrançois Tigeot 	for (i = 0; i < 4; i++) {
2688a3faafcbSSascha Wildner 		int width = 0, height;
26895718399fSFrançois Tigeot 		cvt = &(timing->data.other_data.data.cvt[i]);
26905718399fSFrançois Tigeot 
26915718399fSFrançois Tigeot 		if (!memcmp(cvt->code, empty, 3))
26925718399fSFrançois Tigeot 			continue;
26935718399fSFrançois Tigeot 
26945718399fSFrançois Tigeot 		height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
26955718399fSFrançois Tigeot 		switch (cvt->code[1] & 0x0c) {
26965718399fSFrançois Tigeot 		case 0x00:
26975718399fSFrançois Tigeot 			width = height * 4 / 3;
26985718399fSFrançois Tigeot 			break;
26995718399fSFrançois Tigeot 		case 0x04:
27005718399fSFrançois Tigeot 			width = height * 16 / 9;
27015718399fSFrançois Tigeot 			break;
27025718399fSFrançois Tigeot 		case 0x08:
27035718399fSFrançois Tigeot 			width = height * 16 / 10;
27045718399fSFrançois Tigeot 			break;
27055718399fSFrançois Tigeot 		case 0x0c:
27065718399fSFrançois Tigeot 			width = height * 15 / 9;
27075718399fSFrançois Tigeot 			break;
27085718399fSFrançois Tigeot 		}
27095718399fSFrançois Tigeot 
27105718399fSFrançois Tigeot 		for (j = 1; j < 5; j++) {
27115718399fSFrançois Tigeot 			if (cvt->code[2] & (1 << j)) {
27125718399fSFrançois Tigeot 				newmode = drm_cvt_mode(dev, width, height,
27135718399fSFrançois Tigeot 						       rates[j], j == 0,
27145718399fSFrançois Tigeot 						       false, false);
27155718399fSFrançois Tigeot 				if (newmode) {
27165718399fSFrançois Tigeot 					drm_mode_probed_add(connector, newmode);
27175718399fSFrançois Tigeot 					modes++;
27185718399fSFrançois Tigeot 				}
27195718399fSFrançois Tigeot 			}
27205718399fSFrançois Tigeot 		}
27215718399fSFrançois Tigeot 	}
27225718399fSFrançois Tigeot 
27235718399fSFrançois Tigeot 	return modes;
27245718399fSFrançois Tigeot }
27255718399fSFrançois Tigeot 
27265718399fSFrançois Tigeot static void
do_cvt_mode(struct detailed_timing * timing,void * c)27275718399fSFrançois Tigeot do_cvt_mode(struct detailed_timing *timing, void *c)
27285718399fSFrançois Tigeot {
27295718399fSFrançois Tigeot 	struct detailed_mode_closure *closure = c;
27305718399fSFrançois Tigeot 	struct detailed_non_pixel *data = &timing->data.other_data;
27315718399fSFrançois Tigeot 
27325718399fSFrançois Tigeot 	if (data->type == EDID_DETAIL_CVT_3BYTE)
27335718399fSFrançois Tigeot 		closure->modes += drm_cvt_modes(closure->connector, timing);
27345718399fSFrançois Tigeot }
27355718399fSFrançois Tigeot 
27365718399fSFrançois Tigeot static int
add_cvt_modes(struct drm_connector * connector,struct edid * edid)27375718399fSFrançois Tigeot add_cvt_modes(struct drm_connector *connector, struct edid *edid)
27385718399fSFrançois Tigeot {
27395718399fSFrançois Tigeot 	struct detailed_mode_closure closure = {
274055a9fa67Szrj 		.connector = connector,
274155a9fa67Szrj 		.edid = edid,
27425718399fSFrançois Tigeot 	};
27435718399fSFrançois Tigeot 
27445718399fSFrançois Tigeot 	if (version_greater(edid, 1, 2))
27455718399fSFrançois Tigeot 		drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
27465718399fSFrançois Tigeot 
27475718399fSFrançois Tigeot 	/* XXX should also look for CVT codes in VTB blocks */
27485718399fSFrançois Tigeot 
27495718399fSFrançois Tigeot 	return closure.modes;
27505718399fSFrançois Tigeot }
27515718399fSFrançois Tigeot 
2752352ff8bdSFrançois Tigeot static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode);
2753352ff8bdSFrançois Tigeot 
27545718399fSFrançois Tigeot static void
do_detailed_mode(struct detailed_timing * timing,void * c)27555718399fSFrançois Tigeot do_detailed_mode(struct detailed_timing *timing, void *c)
27565718399fSFrançois Tigeot {
27575718399fSFrançois Tigeot 	struct detailed_mode_closure *closure = c;
27585718399fSFrançois Tigeot 	struct drm_display_mode *newmode;
27595718399fSFrançois Tigeot 
27605718399fSFrançois Tigeot 	if (timing->pixel_clock) {
27615718399fSFrançois Tigeot 		newmode = drm_mode_detailed(closure->connector->dev,
27625718399fSFrançois Tigeot 					    closure->edid, timing,
27635718399fSFrançois Tigeot 					    closure->quirks);
27645718399fSFrançois Tigeot 		if (!newmode)
27655718399fSFrançois Tigeot 			return;
27665718399fSFrançois Tigeot 
27675718399fSFrançois Tigeot 		if (closure->preferred)
27685718399fSFrançois Tigeot 			newmode->type |= DRM_MODE_TYPE_PREFERRED;
27695718399fSFrançois Tigeot 
2770352ff8bdSFrançois Tigeot 		/*
2771352ff8bdSFrançois Tigeot 		 * Detailed modes are limited to 10kHz pixel clock resolution,
2772352ff8bdSFrançois Tigeot 		 * so fix up anything that looks like CEA/HDMI mode, but the clock
2773352ff8bdSFrançois Tigeot 		 * is just slightly off.
2774352ff8bdSFrançois Tigeot 		 */
2775352ff8bdSFrançois Tigeot 		fixup_detailed_cea_mode_clock(newmode);
2776352ff8bdSFrançois Tigeot 
27775718399fSFrançois Tigeot 		drm_mode_probed_add(closure->connector, newmode);
27785718399fSFrançois Tigeot 		closure->modes++;
27795718399fSFrançois Tigeot 		closure->preferred = 0;
27805718399fSFrançois Tigeot 	}
27815718399fSFrançois Tigeot }
27825718399fSFrançois Tigeot 
27835718399fSFrançois Tigeot /*
27845718399fSFrançois Tigeot  * add_detailed_modes - Add modes from detailed timings
27855718399fSFrançois Tigeot  * @connector: attached connector
27865718399fSFrançois Tigeot  * @edid: EDID block to scan
27875718399fSFrançois Tigeot  * @quirks: quirks to apply
27885718399fSFrançois Tigeot  */
27895718399fSFrançois Tigeot static int
add_detailed_modes(struct drm_connector * connector,struct edid * edid,u32 quirks)27905718399fSFrançois Tigeot add_detailed_modes(struct drm_connector *connector, struct edid *edid,
27915718399fSFrançois Tigeot 		   u32 quirks)
27925718399fSFrançois Tigeot {
27935718399fSFrançois Tigeot 	struct detailed_mode_closure closure = {
279455a9fa67Szrj 		.connector = connector,
279555a9fa67Szrj 		.edid = edid,
279655a9fa67Szrj 		.preferred = 1,
279755a9fa67Szrj 		.quirks = quirks,
27985718399fSFrançois Tigeot 	};
27995718399fSFrançois Tigeot 
28005718399fSFrançois Tigeot 	if (closure.preferred && !version_greater(edid, 1, 3))
28015718399fSFrançois Tigeot 		closure.preferred =
28025718399fSFrançois Tigeot 		    (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
28035718399fSFrançois Tigeot 
28045718399fSFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
28055718399fSFrançois Tigeot 
28065718399fSFrançois Tigeot 	return closure.modes;
28075718399fSFrançois Tigeot }
28085718399fSFrançois Tigeot 
28095718399fSFrançois Tigeot #define AUDIO_BLOCK	0x01
28106e29dde8SFrançois Tigeot #define VIDEO_BLOCK     0x02
28115718399fSFrançois Tigeot #define VENDOR_BLOCK    0x03
28125718399fSFrançois Tigeot #define SPEAKER_BLOCK	0x04
2813*3f2dd94aSFrançois Tigeot #define USE_EXTENDED_TAG 0x07
2814*3f2dd94aSFrançois Tigeot #define EXT_VIDEO_CAPABILITY_BLOCK 0x00
2815*3f2dd94aSFrançois Tigeot #define EXT_VIDEO_DATA_BLOCK_420	0x0E
2816*3f2dd94aSFrançois Tigeot #define EXT_VIDEO_CAP_BLOCK_Y420CMDB 0x0F
28175718399fSFrançois Tigeot #define EDID_BASIC_AUDIO	(1 << 6)
28186e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB444	(1 << 5)
28196e29dde8SFrançois Tigeot #define EDID_CEA_YCRCB422	(1 << 4)
2820a2fdbec6SFrançois Tigeot #define EDID_CEA_VCDB_QS	(1 << 6)
28215718399fSFrançois Tigeot 
28229edbd4a0SFrançois Tigeot /*
28235718399fSFrançois Tigeot  * Search EDID for CEA extension block.
28245718399fSFrançois Tigeot  */
drm_find_edid_extension(const struct edid * edid,int ext_id)2825*3f2dd94aSFrançois Tigeot static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
28265718399fSFrançois Tigeot {
28275718399fSFrançois Tigeot 	u8 *edid_ext = NULL;
28285718399fSFrançois Tigeot 	int i;
28295718399fSFrançois Tigeot 
28305718399fSFrançois Tigeot 	/* No EDID or EDID extensions */
28315718399fSFrançois Tigeot 	if (edid == NULL || edid->extensions == 0)
28325718399fSFrançois Tigeot 		return NULL;
28335718399fSFrançois Tigeot 
28345718399fSFrançois Tigeot 	/* Find CEA extension */
28355718399fSFrançois Tigeot 	for (i = 0; i < edid->extensions; i++) {
28365718399fSFrançois Tigeot 		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
28378621f407SFrançois Tigeot 		if (edid_ext[0] == ext_id)
28385718399fSFrançois Tigeot 			break;
28395718399fSFrançois Tigeot 	}
28405718399fSFrançois Tigeot 
28415718399fSFrançois Tigeot 	if (i == edid->extensions)
28425718399fSFrançois Tigeot 		return NULL;
28435718399fSFrançois Tigeot 
28445718399fSFrançois Tigeot 	return edid_ext;
28455718399fSFrançois Tigeot }
28469edbd4a0SFrançois Tigeot 
drm_find_cea_extension(const struct edid * edid)2847*3f2dd94aSFrançois Tigeot static u8 *drm_find_cea_extension(const struct edid *edid)
28488621f407SFrançois Tigeot {
28498621f407SFrançois Tigeot 	return drm_find_edid_extension(edid, CEA_EXT);
28508621f407SFrançois Tigeot }
28518621f407SFrançois Tigeot 
drm_find_displayid_extension(const struct edid * edid)2852*3f2dd94aSFrançois Tigeot static u8 *drm_find_displayid_extension(const struct edid *edid)
28538621f407SFrançois Tigeot {
28548621f407SFrançois Tigeot 	return drm_find_edid_extension(edid, DISPLAYID_EXT);
28558621f407SFrançois Tigeot }
28568621f407SFrançois Tigeot 
28579edbd4a0SFrançois Tigeot /*
28589edbd4a0SFrançois Tigeot  * Calculate the alternate clock for the CEA mode
28599edbd4a0SFrançois Tigeot  * (60Hz vs. 59.94Hz etc.)
28609edbd4a0SFrançois Tigeot  */
28619edbd4a0SFrançois Tigeot static unsigned int
cea_mode_alternate_clock(const struct drm_display_mode * cea_mode)28629edbd4a0SFrançois Tigeot cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
28639edbd4a0SFrançois Tigeot {
28649edbd4a0SFrançois Tigeot 	unsigned int clock = cea_mode->clock;
28659edbd4a0SFrançois Tigeot 
28669edbd4a0SFrançois Tigeot 	if (cea_mode->vrefresh % 6 != 0)
28679edbd4a0SFrançois Tigeot 		return clock;
28689edbd4a0SFrançois Tigeot 
28699edbd4a0SFrançois Tigeot 	/*
28709edbd4a0SFrançois Tigeot 	 * edid_cea_modes contains the 59.94Hz
28719edbd4a0SFrançois Tigeot 	 * variant for 240 and 480 line modes,
28729edbd4a0SFrançois Tigeot 	 * and the 60Hz variant otherwise.
28739edbd4a0SFrançois Tigeot 	 */
28749edbd4a0SFrançois Tigeot 	if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
2875352ff8bdSFrançois Tigeot 		clock = DIV_ROUND_CLOSEST(clock * 1001, 1000);
28769edbd4a0SFrançois Tigeot 	else
2877352ff8bdSFrançois Tigeot 		clock = DIV_ROUND_CLOSEST(clock * 1000, 1001);
28789edbd4a0SFrançois Tigeot 
28799edbd4a0SFrançois Tigeot 	return clock;
28809edbd4a0SFrançois Tigeot }
28815718399fSFrançois Tigeot 
28824be47400SFrançois Tigeot static bool
cea_mode_alternate_timings(u8 vic,struct drm_display_mode * mode)28834be47400SFrançois Tigeot cea_mode_alternate_timings(u8 vic, struct drm_display_mode *mode)
28844be47400SFrançois Tigeot {
28854be47400SFrançois Tigeot 	/*
28864be47400SFrançois Tigeot 	 * For certain VICs the spec allows the vertical
28874be47400SFrançois Tigeot 	 * front porch to vary by one or two lines.
28884be47400SFrançois Tigeot 	 *
28894be47400SFrançois Tigeot 	 * cea_modes[] stores the variant with the shortest
28904be47400SFrançois Tigeot 	 * vertical front porch. We can adjust the mode to
28914be47400SFrançois Tigeot 	 * get the other variants by simply increasing the
28924be47400SFrançois Tigeot 	 * vertical front porch length.
28934be47400SFrançois Tigeot 	 */
28944be47400SFrançois Tigeot 	BUILD_BUG_ON(edid_cea_modes[8].vtotal != 262 ||
28954be47400SFrançois Tigeot 		     edid_cea_modes[9].vtotal != 262 ||
28964be47400SFrançois Tigeot 		     edid_cea_modes[12].vtotal != 262 ||
28974be47400SFrançois Tigeot 		     edid_cea_modes[13].vtotal != 262 ||
28984be47400SFrançois Tigeot 		     edid_cea_modes[23].vtotal != 312 ||
28994be47400SFrançois Tigeot 		     edid_cea_modes[24].vtotal != 312 ||
29004be47400SFrançois Tigeot 		     edid_cea_modes[27].vtotal != 312 ||
29014be47400SFrançois Tigeot 		     edid_cea_modes[28].vtotal != 312);
29024be47400SFrançois Tigeot 
29034be47400SFrançois Tigeot 	if (((vic == 8 || vic == 9 ||
29044be47400SFrançois Tigeot 	      vic == 12 || vic == 13) && mode->vtotal < 263) ||
29054be47400SFrançois Tigeot 	    ((vic == 23 || vic == 24 ||
29064be47400SFrançois Tigeot 	      vic == 27 || vic == 28) && mode->vtotal < 314)) {
29074be47400SFrançois Tigeot 		mode->vsync_start++;
29084be47400SFrançois Tigeot 		mode->vsync_end++;
29094be47400SFrançois Tigeot 		mode->vtotal++;
29104be47400SFrançois Tigeot 
29114be47400SFrançois Tigeot 		return true;
29124be47400SFrançois Tigeot 	}
29134be47400SFrançois Tigeot 
29144be47400SFrançois Tigeot 	return false;
29154be47400SFrançois Tigeot }
29164be47400SFrançois Tigeot 
drm_match_cea_mode_clock_tolerance(const struct drm_display_mode * to_match,unsigned int clock_tolerance)2917aee94f86SFrançois Tigeot static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
2918aee94f86SFrançois Tigeot 					     unsigned int clock_tolerance)
2919aee94f86SFrançois Tigeot {
2920aee94f86SFrançois Tigeot 	u8 vic;
2921aee94f86SFrançois Tigeot 
2922aee94f86SFrançois Tigeot 	if (!to_match->clock)
2923aee94f86SFrançois Tigeot 		return 0;
2924aee94f86SFrançois Tigeot 
2925aee94f86SFrançois Tigeot 	for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) {
29264be47400SFrançois Tigeot 		struct drm_display_mode cea_mode = edid_cea_modes[vic];
2927aee94f86SFrançois Tigeot 		unsigned int clock1, clock2;
2928aee94f86SFrançois Tigeot 
2929aee94f86SFrançois Tigeot 		/* Check both 60Hz and 59.94Hz */
29304be47400SFrançois Tigeot 		clock1 = cea_mode.clock;
29314be47400SFrançois Tigeot 		clock2 = cea_mode_alternate_clock(&cea_mode);
2932aee94f86SFrançois Tigeot 
2933aee94f86SFrançois Tigeot 		if (abs(to_match->clock - clock1) > clock_tolerance &&
2934aee94f86SFrançois Tigeot 		    abs(to_match->clock - clock2) > clock_tolerance)
2935aee94f86SFrançois Tigeot 			continue;
2936aee94f86SFrançois Tigeot 
29374be47400SFrançois Tigeot 		do {
29384be47400SFrançois Tigeot 			if (drm_mode_equal_no_clocks_no_stereo(to_match, &cea_mode))
2939aee94f86SFrançois Tigeot 				return vic;
29404be47400SFrançois Tigeot 		} while (cea_mode_alternate_timings(vic, &cea_mode));
2941aee94f86SFrançois Tigeot 	}
2942aee94f86SFrançois Tigeot 
2943aee94f86SFrançois Tigeot 	return 0;
2944aee94f86SFrançois Tigeot }
2945aee94f86SFrançois Tigeot 
2946d82bf20eSFrançois Tigeot /**
2947d82bf20eSFrançois Tigeot  * drm_match_cea_mode - look for a CEA mode matching given mode
2948d82bf20eSFrançois Tigeot  * @to_match: display mode
2949d82bf20eSFrançois Tigeot  *
2950ba55f2f5SFrançois Tigeot  * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
2951d82bf20eSFrançois Tigeot  * mode.
29526e29dde8SFrançois Tigeot  */
drm_match_cea_mode(const struct drm_display_mode * to_match)2953a2fdbec6SFrançois Tigeot u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
29545718399fSFrançois Tigeot {
2955aee94f86SFrançois Tigeot 	u8 vic;
29566e29dde8SFrançois Tigeot 
29579edbd4a0SFrançois Tigeot 	if (!to_match->clock)
29589edbd4a0SFrançois Tigeot 		return 0;
29596e29dde8SFrançois Tigeot 
2960aee94f86SFrançois Tigeot 	for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) {
29614be47400SFrançois Tigeot 		struct drm_display_mode cea_mode = edid_cea_modes[vic];
29629edbd4a0SFrançois Tigeot 		unsigned int clock1, clock2;
29639edbd4a0SFrançois Tigeot 
29649edbd4a0SFrançois Tigeot 		/* Check both 60Hz and 59.94Hz */
29654be47400SFrançois Tigeot 		clock1 = cea_mode.clock;
29664be47400SFrançois Tigeot 		clock2 = cea_mode_alternate_clock(&cea_mode);
29679edbd4a0SFrançois Tigeot 
29684be47400SFrançois Tigeot 		if (KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock1) &&
29694be47400SFrançois Tigeot 		    KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock2))
29704be47400SFrançois Tigeot 			continue;
29714be47400SFrançois Tigeot 
29724be47400SFrançois Tigeot 		do {
29734be47400SFrançois Tigeot 			if (drm_mode_equal_no_clocks_no_stereo(to_match, &cea_mode))
2974aee94f86SFrançois Tigeot 				return vic;
29754be47400SFrançois Tigeot 		} while (cea_mode_alternate_timings(vic, &cea_mode));
29766e29dde8SFrançois Tigeot 	}
29774be47400SFrançois Tigeot 
29786e29dde8SFrançois Tigeot 	return 0;
29796e29dde8SFrançois Tigeot }
29806e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_match_cea_mode);
29816e29dde8SFrançois Tigeot 
drm_valid_cea_vic(u8 vic)2982aee94f86SFrançois Tigeot static bool drm_valid_cea_vic(u8 vic)
2983aee94f86SFrançois Tigeot {
2984aee94f86SFrançois Tigeot 	return vic > 0 && vic < ARRAY_SIZE(edid_cea_modes);
2985aee94f86SFrançois Tigeot }
2986aee94f86SFrançois Tigeot 
2987ba55f2f5SFrançois Tigeot /**
2988ba55f2f5SFrançois Tigeot  * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
2989ba55f2f5SFrançois Tigeot  * the input VIC from the CEA mode list
2990ba55f2f5SFrançois Tigeot  * @video_code: ID given to each of the CEA modes
2991ba55f2f5SFrançois Tigeot  *
2992ba55f2f5SFrançois Tigeot  * Returns picture aspect ratio
2993ba55f2f5SFrançois Tigeot  */
drm_get_cea_aspect_ratio(const u8 video_code)2994ba55f2f5SFrançois Tigeot enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
2995ba55f2f5SFrançois Tigeot {
2996aee94f86SFrançois Tigeot 	return edid_cea_modes[video_code].picture_aspect_ratio;
2997ba55f2f5SFrançois Tigeot }
2998ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
2999ba55f2f5SFrançois Tigeot 
30009edbd4a0SFrançois Tigeot /*
30019edbd4a0SFrançois Tigeot  * Calculate the alternate clock for HDMI modes (those from the HDMI vendor
30029edbd4a0SFrançois Tigeot  * specific block).
30039edbd4a0SFrançois Tigeot  *
30049edbd4a0SFrançois Tigeot  * It's almost like cea_mode_alternate_clock(), we just need to add an
30059edbd4a0SFrançois Tigeot  * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this
30069edbd4a0SFrançois Tigeot  * one.
30079edbd4a0SFrançois Tigeot  */
30089edbd4a0SFrançois Tigeot static unsigned int
hdmi_mode_alternate_clock(const struct drm_display_mode * hdmi_mode)30099edbd4a0SFrançois Tigeot hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
30109edbd4a0SFrançois Tigeot {
30119edbd4a0SFrançois Tigeot 	if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160)
30129edbd4a0SFrançois Tigeot 		return hdmi_mode->clock;
30139edbd4a0SFrançois Tigeot 
30149edbd4a0SFrançois Tigeot 	return cea_mode_alternate_clock(hdmi_mode);
30159edbd4a0SFrançois Tigeot }
30169edbd4a0SFrançois Tigeot 
drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode * to_match,unsigned int clock_tolerance)3017aee94f86SFrançois Tigeot static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
3018aee94f86SFrançois Tigeot 					      unsigned int clock_tolerance)
3019aee94f86SFrançois Tigeot {
3020aee94f86SFrançois Tigeot 	u8 vic;
3021aee94f86SFrançois Tigeot 
3022aee94f86SFrançois Tigeot 	if (!to_match->clock)
3023aee94f86SFrançois Tigeot 		return 0;
3024aee94f86SFrançois Tigeot 
3025aee94f86SFrançois Tigeot 	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
3026aee94f86SFrançois Tigeot 		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
3027aee94f86SFrançois Tigeot 		unsigned int clock1, clock2;
3028aee94f86SFrançois Tigeot 
3029aee94f86SFrançois Tigeot 		/* Make sure to also match alternate clocks */
3030aee94f86SFrançois Tigeot 		clock1 = hdmi_mode->clock;
3031aee94f86SFrançois Tigeot 		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
3032aee94f86SFrançois Tigeot 
3033aee94f86SFrançois Tigeot 		if (abs(to_match->clock - clock1) > clock_tolerance &&
3034aee94f86SFrançois Tigeot 		    abs(to_match->clock - clock2) > clock_tolerance)
3035aee94f86SFrançois Tigeot 			continue;
3036aee94f86SFrançois Tigeot 
3037aee94f86SFrançois Tigeot 		if (drm_mode_equal_no_clocks(to_match, hdmi_mode))
3038aee94f86SFrançois Tigeot 			return vic;
3039aee94f86SFrançois Tigeot 	}
3040aee94f86SFrançois Tigeot 
3041aee94f86SFrançois Tigeot 	return 0;
3042aee94f86SFrançois Tigeot }
3043aee94f86SFrançois Tigeot 
30449edbd4a0SFrançois Tigeot /*
30459edbd4a0SFrançois Tigeot  * drm_match_hdmi_mode - look for a HDMI mode matching given mode
30469edbd4a0SFrançois Tigeot  * @to_match: display mode
30479edbd4a0SFrançois Tigeot  *
30489edbd4a0SFrançois Tigeot  * An HDMI mode is one defined in the HDMI vendor specific block.
30499edbd4a0SFrançois Tigeot  *
30509edbd4a0SFrançois Tigeot  * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
30519edbd4a0SFrançois Tigeot  */
drm_match_hdmi_mode(const struct drm_display_mode * to_match)30529edbd4a0SFrançois Tigeot static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
30539edbd4a0SFrançois Tigeot {
3054aee94f86SFrançois Tigeot 	u8 vic;
30559edbd4a0SFrançois Tigeot 
30569edbd4a0SFrançois Tigeot 	if (!to_match->clock)
30579edbd4a0SFrançois Tigeot 		return 0;
30589edbd4a0SFrançois Tigeot 
3059aee94f86SFrançois Tigeot 	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
3060aee94f86SFrançois Tigeot 		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
30619edbd4a0SFrançois Tigeot 		unsigned int clock1, clock2;
30629edbd4a0SFrançois Tigeot 
30639edbd4a0SFrançois Tigeot 		/* Make sure to also match alternate clocks */
30649edbd4a0SFrançois Tigeot 		clock1 = hdmi_mode->clock;
30659edbd4a0SFrançois Tigeot 		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
30669edbd4a0SFrançois Tigeot 
30679edbd4a0SFrançois Tigeot 		if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
30689edbd4a0SFrançois Tigeot 		     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
30699edbd4a0SFrançois Tigeot 		    drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode))
3070aee94f86SFrançois Tigeot 			return vic;
30719edbd4a0SFrançois Tigeot 	}
30729edbd4a0SFrançois Tigeot 	return 0;
30739edbd4a0SFrançois Tigeot }
30746e29dde8SFrançois Tigeot 
drm_valid_hdmi_vic(u8 vic)3075aee94f86SFrançois Tigeot static bool drm_valid_hdmi_vic(u8 vic)
3076aee94f86SFrançois Tigeot {
3077aee94f86SFrançois Tigeot 	return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
3078aee94f86SFrançois Tigeot }
3079aee94f86SFrançois Tigeot 
30806e29dde8SFrançois Tigeot static int
add_alternate_cea_modes(struct drm_connector * connector,struct edid * edid)30819edbd4a0SFrançois Tigeot add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
30826e29dde8SFrançois Tigeot {
30836e29dde8SFrançois Tigeot 	struct drm_device *dev = connector->dev;
30849edbd4a0SFrançois Tigeot 	struct drm_display_mode *mode, *tmp;
30859edbd4a0SFrançois Tigeot 	LINUX_LIST_HEAD(list);
30866e29dde8SFrançois Tigeot 	int modes = 0;
30876e29dde8SFrançois Tigeot 
30889edbd4a0SFrançois Tigeot 	/* Don't add CEA modes if the CEA extension block is missing */
30899edbd4a0SFrançois Tigeot 	if (!drm_find_cea_extension(edid))
30909edbd4a0SFrançois Tigeot 		return 0;
30919edbd4a0SFrançois Tigeot 
30929edbd4a0SFrançois Tigeot 	/*
30939edbd4a0SFrançois Tigeot 	 * Go through all probed modes and create a new mode
30949edbd4a0SFrançois Tigeot 	 * with the alternate clock for certain CEA modes.
30959edbd4a0SFrançois Tigeot 	 */
30969edbd4a0SFrançois Tigeot 	list_for_each_entry(mode, &connector->probed_modes, head) {
30979edbd4a0SFrançois Tigeot 		const struct drm_display_mode *cea_mode = NULL;
30986e29dde8SFrançois Tigeot 		struct drm_display_mode *newmode;
3099aee94f86SFrançois Tigeot 		u8 vic = drm_match_cea_mode(mode);
31009edbd4a0SFrançois Tigeot 		unsigned int clock1, clock2;
31019edbd4a0SFrançois Tigeot 
3102aee94f86SFrançois Tigeot 		if (drm_valid_cea_vic(vic)) {
3103aee94f86SFrançois Tigeot 			cea_mode = &edid_cea_modes[vic];
31049edbd4a0SFrançois Tigeot 			clock2 = cea_mode_alternate_clock(cea_mode);
31059edbd4a0SFrançois Tigeot 		} else {
3106aee94f86SFrançois Tigeot 			vic = drm_match_hdmi_mode(mode);
3107aee94f86SFrançois Tigeot 			if (drm_valid_hdmi_vic(vic)) {
3108aee94f86SFrançois Tigeot 				cea_mode = &edid_4k_modes[vic];
31099edbd4a0SFrançois Tigeot 				clock2 = hdmi_mode_alternate_clock(cea_mode);
31109edbd4a0SFrançois Tigeot 			}
31119edbd4a0SFrançois Tigeot 		}
31129edbd4a0SFrançois Tigeot 
31139edbd4a0SFrançois Tigeot 		if (!cea_mode)
31149edbd4a0SFrançois Tigeot 			continue;
31159edbd4a0SFrançois Tigeot 
31169edbd4a0SFrançois Tigeot 		clock1 = cea_mode->clock;
31179edbd4a0SFrançois Tigeot 
31189edbd4a0SFrançois Tigeot 		if (clock1 == clock2)
31199edbd4a0SFrançois Tigeot 			continue;
31209edbd4a0SFrançois Tigeot 
31219edbd4a0SFrançois Tigeot 		if (mode->clock != clock1 && mode->clock != clock2)
31229edbd4a0SFrançois Tigeot 			continue;
31239edbd4a0SFrançois Tigeot 
31249edbd4a0SFrançois Tigeot 		newmode = drm_mode_duplicate(dev, cea_mode);
31259edbd4a0SFrançois Tigeot 		if (!newmode)
31269edbd4a0SFrançois Tigeot 			continue;
31279edbd4a0SFrançois Tigeot 
31289edbd4a0SFrançois Tigeot 		/* Carry over the stereo flags */
31299edbd4a0SFrançois Tigeot 		newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK;
31309edbd4a0SFrançois Tigeot 
31319edbd4a0SFrançois Tigeot 		/*
31329edbd4a0SFrançois Tigeot 		 * The current mode could be either variant. Make
31339edbd4a0SFrançois Tigeot 		 * sure to pick the "other" clock for the new mode.
31349edbd4a0SFrançois Tigeot 		 */
31359edbd4a0SFrançois Tigeot 		if (mode->clock != clock1)
31369edbd4a0SFrançois Tigeot 			newmode->clock = clock1;
31379edbd4a0SFrançois Tigeot 		else
31389edbd4a0SFrançois Tigeot 			newmode->clock = clock2;
31399edbd4a0SFrançois Tigeot 
31409edbd4a0SFrançois Tigeot 		list_add_tail(&newmode->head, &list);
31419edbd4a0SFrançois Tigeot 	}
31429edbd4a0SFrançois Tigeot 
31439edbd4a0SFrançois Tigeot 	list_for_each_entry_safe(mode, tmp, &list, head) {
31449edbd4a0SFrançois Tigeot 		list_del(&mode->head);
31459edbd4a0SFrançois Tigeot 		drm_mode_probed_add(connector, mode);
31469edbd4a0SFrançois Tigeot 		modes++;
31479edbd4a0SFrançois Tigeot 	}
31489edbd4a0SFrançois Tigeot 
31499edbd4a0SFrançois Tigeot 	return modes;
31509edbd4a0SFrançois Tigeot }
31519edbd4a0SFrançois Tigeot 
svd_to_vic(u8 svd)3152*3f2dd94aSFrançois Tigeot static u8 svd_to_vic(u8 svd)
3153*3f2dd94aSFrançois Tigeot {
3154*3f2dd94aSFrançois Tigeot 	/* 0-6 bit vic, 7th bit native mode indicator */
3155*3f2dd94aSFrançois Tigeot 	if ((svd >= 1 &&  svd <= 64) || (svd >= 129 && svd <= 192))
3156*3f2dd94aSFrançois Tigeot 		return svd & 127;
3157*3f2dd94aSFrançois Tigeot 
3158*3f2dd94aSFrançois Tigeot 	return svd;
3159*3f2dd94aSFrançois Tigeot }
3160*3f2dd94aSFrançois Tigeot 
31619edbd4a0SFrançois Tigeot static struct drm_display_mode *
drm_display_mode_from_vic_index(struct drm_connector * connector,const u8 * video_db,u8 video_len,u8 video_index)31629edbd4a0SFrançois Tigeot drm_display_mode_from_vic_index(struct drm_connector *connector,
31639edbd4a0SFrançois Tigeot 				const u8 *video_db, u8 video_len,
31649edbd4a0SFrançois Tigeot 				u8 video_index)
31659edbd4a0SFrançois Tigeot {
31669edbd4a0SFrançois Tigeot 	struct drm_device *dev = connector->dev;
31679edbd4a0SFrançois Tigeot 	struct drm_display_mode *newmode;
3168aee94f86SFrançois Tigeot 	u8 vic;
31699edbd4a0SFrançois Tigeot 
31709edbd4a0SFrançois Tigeot 	if (video_db == NULL || video_index >= video_len)
31719edbd4a0SFrançois Tigeot 		return NULL;
31729edbd4a0SFrançois Tigeot 
31739edbd4a0SFrançois Tigeot 	/* CEA modes are numbered 1..127 */
3174*3f2dd94aSFrançois Tigeot 	vic = svd_to_vic(video_db[video_index]);
3175aee94f86SFrançois Tigeot 	if (!drm_valid_cea_vic(vic))
31769edbd4a0SFrançois Tigeot 		return NULL;
31779edbd4a0SFrançois Tigeot 
3178aee94f86SFrançois Tigeot 	newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
3179ba55f2f5SFrançois Tigeot 	if (!newmode)
3180ba55f2f5SFrançois Tigeot 		return NULL;
3181ba55f2f5SFrançois Tigeot 
31829edbd4a0SFrançois Tigeot 	newmode->vrefresh = 0;
31839edbd4a0SFrançois Tigeot 
31849edbd4a0SFrançois Tigeot 	return newmode;
31859edbd4a0SFrançois Tigeot }
31869edbd4a0SFrançois Tigeot 
3187*3f2dd94aSFrançois Tigeot /*
3188*3f2dd94aSFrançois Tigeot  * do_y420vdb_modes - Parse YCBCR 420 only modes
3189*3f2dd94aSFrançois Tigeot  * @connector: connector corresponding to the HDMI sink
3190*3f2dd94aSFrançois Tigeot  * @svds: start of the data block of CEA YCBCR 420 VDB
3191*3f2dd94aSFrançois Tigeot  * @len: length of the CEA YCBCR 420 VDB
3192*3f2dd94aSFrançois Tigeot  *
3193*3f2dd94aSFrançois Tigeot  * Parse the CEA-861-F YCBCR 420 Video Data Block (Y420VDB)
3194*3f2dd94aSFrançois Tigeot  * which contains modes which can be supported in YCBCR 420
3195*3f2dd94aSFrançois Tigeot  * output format only.
3196*3f2dd94aSFrançois Tigeot  */
do_y420vdb_modes(struct drm_connector * connector,const u8 * svds,u8 svds_len)3197*3f2dd94aSFrançois Tigeot static int do_y420vdb_modes(struct drm_connector *connector,
3198*3f2dd94aSFrançois Tigeot 			    const u8 *svds, u8 svds_len)
3199*3f2dd94aSFrançois Tigeot {
3200*3f2dd94aSFrançois Tigeot 	int modes = 0, i;
3201*3f2dd94aSFrançois Tigeot 	struct drm_device *dev = connector->dev;
3202*3f2dd94aSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
3203*3f2dd94aSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &info->hdmi;
3204*3f2dd94aSFrançois Tigeot 
3205*3f2dd94aSFrançois Tigeot 	for (i = 0; i < svds_len; i++) {
3206*3f2dd94aSFrançois Tigeot 		u8 vic = svd_to_vic(svds[i]);
3207*3f2dd94aSFrançois Tigeot 		struct drm_display_mode *newmode;
3208*3f2dd94aSFrançois Tigeot 
3209*3f2dd94aSFrançois Tigeot 		if (!drm_valid_cea_vic(vic))
3210*3f2dd94aSFrançois Tigeot 			continue;
3211*3f2dd94aSFrançois Tigeot 
3212*3f2dd94aSFrançois Tigeot 		newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
3213*3f2dd94aSFrançois Tigeot 		if (!newmode)
3214*3f2dd94aSFrançois Tigeot 			break;
3215*3f2dd94aSFrançois Tigeot 		bitmap_set(hdmi->y420_vdb_modes, vic, 1);
3216*3f2dd94aSFrançois Tigeot 		drm_mode_probed_add(connector, newmode);
3217*3f2dd94aSFrançois Tigeot 		modes++;
3218*3f2dd94aSFrançois Tigeot 	}
3219*3f2dd94aSFrançois Tigeot 
3220*3f2dd94aSFrançois Tigeot 	if (modes > 0)
3221*3f2dd94aSFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
3222*3f2dd94aSFrançois Tigeot 	return modes;
3223*3f2dd94aSFrançois Tigeot }
3224*3f2dd94aSFrançois Tigeot 
3225*3f2dd94aSFrançois Tigeot /*
3226*3f2dd94aSFrançois Tigeot  * drm_add_cmdb_modes - Add a YCBCR 420 mode into bitmap
3227*3f2dd94aSFrançois Tigeot  * @connector: connector corresponding to the HDMI sink
3228*3f2dd94aSFrançois Tigeot  * @vic: CEA vic for the video mode to be added in the map
3229*3f2dd94aSFrançois Tigeot  *
3230*3f2dd94aSFrançois Tigeot  * Makes an entry for a videomode in the YCBCR 420 bitmap
3231*3f2dd94aSFrançois Tigeot  */
3232*3f2dd94aSFrançois Tigeot static void
drm_add_cmdb_modes(struct drm_connector * connector,u8 svd)3233*3f2dd94aSFrançois Tigeot drm_add_cmdb_modes(struct drm_connector *connector, u8 svd)
3234*3f2dd94aSFrançois Tigeot {
3235*3f2dd94aSFrançois Tigeot 	u8 vic = svd_to_vic(svd);
3236*3f2dd94aSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
3237*3f2dd94aSFrançois Tigeot 
3238*3f2dd94aSFrançois Tigeot 	if (!drm_valid_cea_vic(vic))
3239*3f2dd94aSFrançois Tigeot 		return;
3240*3f2dd94aSFrançois Tigeot 
3241*3f2dd94aSFrançois Tigeot 	bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
3242*3f2dd94aSFrançois Tigeot }
3243*3f2dd94aSFrançois Tigeot 
32449edbd4a0SFrançois Tigeot static int
do_cea_modes(struct drm_connector * connector,const u8 * db,u8 len)32459edbd4a0SFrançois Tigeot do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
32469edbd4a0SFrançois Tigeot {
32479edbd4a0SFrançois Tigeot 	int i, modes = 0;
3248*3f2dd94aSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
32499edbd4a0SFrançois Tigeot 
32509edbd4a0SFrançois Tigeot 	for (i = 0; i < len; i++) {
32519edbd4a0SFrançois Tigeot 		struct drm_display_mode *mode;
32529edbd4a0SFrançois Tigeot 		mode = drm_display_mode_from_vic_index(connector, db, len, i);
32539edbd4a0SFrançois Tigeot 		if (mode) {
3254*3f2dd94aSFrançois Tigeot 			/*
3255*3f2dd94aSFrançois Tigeot 			 * YCBCR420 capability block contains a bitmap which
3256*3f2dd94aSFrançois Tigeot 			 * gives the index of CEA modes from CEA VDB, which
3257*3f2dd94aSFrançois Tigeot 			 * can support YCBCR 420 sampling output also (apart
3258*3f2dd94aSFrançois Tigeot 			 * from RGB/YCBCR444 etc).
3259*3f2dd94aSFrançois Tigeot 			 * For example, if the bit 0 in bitmap is set,
3260*3f2dd94aSFrançois Tigeot 			 * first mode in VDB can support YCBCR420 output too.
3261*3f2dd94aSFrançois Tigeot 			 * Add YCBCR420 modes only if sink is HDMI 2.0 capable.
3262*3f2dd94aSFrançois Tigeot 			 */
3263*3f2dd94aSFrançois Tigeot 			if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i))
3264*3f2dd94aSFrançois Tigeot 				drm_add_cmdb_modes(connector, db[i]);
3265*3f2dd94aSFrançois Tigeot 
32669edbd4a0SFrançois Tigeot 			drm_mode_probed_add(connector, mode);
32679edbd4a0SFrançois Tigeot 			modes++;
32689edbd4a0SFrançois Tigeot 		}
32699edbd4a0SFrançois Tigeot 	}
32709edbd4a0SFrançois Tigeot 
32719edbd4a0SFrançois Tigeot 	return modes;
32729edbd4a0SFrançois Tigeot }
32739edbd4a0SFrançois Tigeot 
32749edbd4a0SFrançois Tigeot struct stereo_mandatory_mode {
32759edbd4a0SFrançois Tigeot 	int width, height, vrefresh;
32769edbd4a0SFrançois Tigeot 	unsigned int flags;
32779edbd4a0SFrançois Tigeot };
32789edbd4a0SFrançois Tigeot 
32799edbd4a0SFrançois Tigeot static const struct stereo_mandatory_mode stereo_mandatory_modes[] = {
32809edbd4a0SFrançois Tigeot 	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
32819edbd4a0SFrançois Tigeot 	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING },
32829edbd4a0SFrançois Tigeot 	{ 1920, 1080, 50,
32839edbd4a0SFrançois Tigeot 	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
32849edbd4a0SFrançois Tigeot 	{ 1920, 1080, 60,
32859edbd4a0SFrançois Tigeot 	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
32869edbd4a0SFrançois Tigeot 	{ 1280, 720,  50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
32879edbd4a0SFrançois Tigeot 	{ 1280, 720,  50, DRM_MODE_FLAG_3D_FRAME_PACKING },
32889edbd4a0SFrançois Tigeot 	{ 1280, 720,  60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
32899edbd4a0SFrançois Tigeot 	{ 1280, 720,  60, DRM_MODE_FLAG_3D_FRAME_PACKING }
32909edbd4a0SFrançois Tigeot };
32919edbd4a0SFrançois Tigeot 
32929edbd4a0SFrançois Tigeot static bool
stereo_match_mandatory(const struct drm_display_mode * mode,const struct stereo_mandatory_mode * stereo_mode)32939edbd4a0SFrançois Tigeot stereo_match_mandatory(const struct drm_display_mode *mode,
32949edbd4a0SFrançois Tigeot 		       const struct stereo_mandatory_mode *stereo_mode)
32959edbd4a0SFrançois Tigeot {
32969edbd4a0SFrançois Tigeot 	unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
32979edbd4a0SFrançois Tigeot 
32989edbd4a0SFrançois Tigeot 	return mode->hdisplay == stereo_mode->width &&
32999edbd4a0SFrançois Tigeot 	       mode->vdisplay == stereo_mode->height &&
33009edbd4a0SFrançois Tigeot 	       interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) &&
33019edbd4a0SFrançois Tigeot 	       drm_mode_vrefresh(mode) == stereo_mode->vrefresh;
33029edbd4a0SFrançois Tigeot }
33039edbd4a0SFrançois Tigeot 
add_hdmi_mandatory_stereo_modes(struct drm_connector * connector)33049edbd4a0SFrançois Tigeot static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector)
33059edbd4a0SFrançois Tigeot {
33069edbd4a0SFrançois Tigeot 	struct drm_device *dev = connector->dev;
33071e12ee3bSFrançois Tigeot 	const struct drm_display_mode *mode;
33089edbd4a0SFrançois Tigeot 	struct list_head stereo_modes;
33099edbd4a0SFrançois Tigeot 	int modes = 0, i;
33109edbd4a0SFrançois Tigeot 
33119edbd4a0SFrançois Tigeot 	INIT_LIST_HEAD(&stereo_modes);
33129edbd4a0SFrançois Tigeot 
33139edbd4a0SFrançois Tigeot 	list_for_each_entry(mode, &connector->probed_modes, head) {
33149edbd4a0SFrançois Tigeot 		for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) {
33159edbd4a0SFrançois Tigeot 			const struct stereo_mandatory_mode *mandatory;
33169edbd4a0SFrançois Tigeot 			struct drm_display_mode *new_mode;
33179edbd4a0SFrançois Tigeot 
33189edbd4a0SFrançois Tigeot 			if (!stereo_match_mandatory(mode,
33199edbd4a0SFrançois Tigeot 						    &stereo_mandatory_modes[i]))
33209edbd4a0SFrançois Tigeot 				continue;
33219edbd4a0SFrançois Tigeot 
33229edbd4a0SFrançois Tigeot 			mandatory = &stereo_mandatory_modes[i];
33239edbd4a0SFrançois Tigeot 			new_mode = drm_mode_duplicate(dev, mode);
33249edbd4a0SFrançois Tigeot 			if (!new_mode)
33259edbd4a0SFrançois Tigeot 				continue;
33269edbd4a0SFrançois Tigeot 
33279edbd4a0SFrançois Tigeot 			new_mode->flags |= mandatory->flags;
33289edbd4a0SFrançois Tigeot 			list_add_tail(&new_mode->head, &stereo_modes);
33299edbd4a0SFrançois Tigeot 			modes++;
33309edbd4a0SFrançois Tigeot 		}
33319edbd4a0SFrançois Tigeot 	}
33329edbd4a0SFrançois Tigeot 
33339edbd4a0SFrançois Tigeot 	list_splice_tail(&stereo_modes, &connector->probed_modes);
33349edbd4a0SFrançois Tigeot 
33359edbd4a0SFrançois Tigeot 	return modes;
33369edbd4a0SFrançois Tigeot }
33379edbd4a0SFrançois Tigeot 
add_hdmi_mode(struct drm_connector * connector,u8 vic)33389edbd4a0SFrançois Tigeot static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
33399edbd4a0SFrançois Tigeot {
33409edbd4a0SFrançois Tigeot 	struct drm_device *dev = connector->dev;
33419edbd4a0SFrançois Tigeot 	struct drm_display_mode *newmode;
33429edbd4a0SFrançois Tigeot 
3343aee94f86SFrançois Tigeot 	if (!drm_valid_hdmi_vic(vic)) {
33449edbd4a0SFrançois Tigeot 		DRM_ERROR("Unknown HDMI VIC: %d\n", vic);
33459edbd4a0SFrançois Tigeot 		return 0;
33469edbd4a0SFrançois Tigeot 	}
33479edbd4a0SFrançois Tigeot 
33489edbd4a0SFrançois Tigeot 	newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
33499edbd4a0SFrançois Tigeot 	if (!newmode)
33509edbd4a0SFrançois Tigeot 		return 0;
33519edbd4a0SFrançois Tigeot 
33529edbd4a0SFrançois Tigeot 	drm_mode_probed_add(connector, newmode);
33539edbd4a0SFrançois Tigeot 
33549edbd4a0SFrançois Tigeot 	return 1;
33559edbd4a0SFrançois Tigeot }
33569edbd4a0SFrançois Tigeot 
add_3d_struct_modes(struct drm_connector * connector,u16 structure,const u8 * video_db,u8 video_len,u8 video_index)33579edbd4a0SFrançois Tigeot static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
33589edbd4a0SFrançois Tigeot 			       const u8 *video_db, u8 video_len, u8 video_index)
33599edbd4a0SFrançois Tigeot {
33609edbd4a0SFrançois Tigeot 	struct drm_display_mode *newmode;
33619edbd4a0SFrançois Tigeot 	int modes = 0;
33629edbd4a0SFrançois Tigeot 
33639edbd4a0SFrançois Tigeot 	if (structure & (1 << 0)) {
33649edbd4a0SFrançois Tigeot 		newmode = drm_display_mode_from_vic_index(connector, video_db,
33659edbd4a0SFrançois Tigeot 							  video_len,
33669edbd4a0SFrançois Tigeot 							  video_index);
33676e29dde8SFrançois Tigeot 		if (newmode) {
33689edbd4a0SFrançois Tigeot 			newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
33696e29dde8SFrançois Tigeot 			drm_mode_probed_add(connector, newmode);
33706e29dde8SFrançois Tigeot 			modes++;
33716e29dde8SFrançois Tigeot 		}
33726e29dde8SFrançois Tigeot 	}
33739edbd4a0SFrançois Tigeot 	if (structure & (1 << 6)) {
33749edbd4a0SFrançois Tigeot 		newmode = drm_display_mode_from_vic_index(connector, video_db,
33759edbd4a0SFrançois Tigeot 							  video_len,
33769edbd4a0SFrançois Tigeot 							  video_index);
33779edbd4a0SFrançois Tigeot 		if (newmode) {
33789edbd4a0SFrançois Tigeot 			newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
33799edbd4a0SFrançois Tigeot 			drm_mode_probed_add(connector, newmode);
33809edbd4a0SFrançois Tigeot 			modes++;
33819edbd4a0SFrançois Tigeot 		}
33829edbd4a0SFrançois Tigeot 	}
33839edbd4a0SFrançois Tigeot 	if (structure & (1 << 8)) {
33849edbd4a0SFrançois Tigeot 		newmode = drm_display_mode_from_vic_index(connector, video_db,
33859edbd4a0SFrançois Tigeot 							  video_len,
33869edbd4a0SFrançois Tigeot 							  video_index);
33879edbd4a0SFrançois Tigeot 		if (newmode) {
33889edbd4a0SFrançois Tigeot 			newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
33899edbd4a0SFrançois Tigeot 			drm_mode_probed_add(connector, newmode);
33909edbd4a0SFrançois Tigeot 			modes++;
33919edbd4a0SFrançois Tigeot 		}
33926e29dde8SFrançois Tigeot 	}
33936e29dde8SFrançois Tigeot 
33946e29dde8SFrançois Tigeot 	return modes;
33956e29dde8SFrançois Tigeot }
33966e29dde8SFrançois Tigeot 
33979edbd4a0SFrançois Tigeot /*
33989edbd4a0SFrançois Tigeot  * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
33999edbd4a0SFrançois Tigeot  * @connector: connector corresponding to the HDMI sink
34009edbd4a0SFrançois Tigeot  * @db: start of the CEA vendor specific block
34019edbd4a0SFrançois Tigeot  * @len: length of the CEA block payload, ie. one can access up to db[len]
34029edbd4a0SFrançois Tigeot  *
34039edbd4a0SFrançois Tigeot  * Parses the HDMI VSDB looking for modes to add to @connector. This function
34049edbd4a0SFrançois Tigeot  * also adds the stereo 3d modes when applicable.
34059edbd4a0SFrançois Tigeot  */
34069edbd4a0SFrançois Tigeot static int
do_hdmi_vsdb_modes(struct drm_connector * connector,const u8 * db,u8 len,const u8 * video_db,u8 video_len)34079edbd4a0SFrançois Tigeot do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
34089edbd4a0SFrançois Tigeot 		   const u8 *video_db, u8 video_len)
34099edbd4a0SFrançois Tigeot {
34109edbd4a0SFrançois Tigeot 	int modes = 0, offset = 0, i, multi_present = 0, multi_len;
34119edbd4a0SFrançois Tigeot 	u8 vic_len, hdmi_3d_len = 0;
34129edbd4a0SFrançois Tigeot 	u16 mask;
34139edbd4a0SFrançois Tigeot 	u16 structure_all;
34149edbd4a0SFrançois Tigeot 
34159edbd4a0SFrançois Tigeot 	if (len < 8)
34169edbd4a0SFrançois Tigeot 		goto out;
34179edbd4a0SFrançois Tigeot 
34189edbd4a0SFrançois Tigeot 	/* no HDMI_Video_Present */
34199edbd4a0SFrançois Tigeot 	if (!(db[8] & (1 << 5)))
34209edbd4a0SFrançois Tigeot 		goto out;
34219edbd4a0SFrançois Tigeot 
34229edbd4a0SFrançois Tigeot 	/* Latency_Fields_Present */
34239edbd4a0SFrançois Tigeot 	if (db[8] & (1 << 7))
34249edbd4a0SFrançois Tigeot 		offset += 2;
34259edbd4a0SFrançois Tigeot 
34269edbd4a0SFrançois Tigeot 	/* I_Latency_Fields_Present */
34279edbd4a0SFrançois Tigeot 	if (db[8] & (1 << 6))
34289edbd4a0SFrançois Tigeot 		offset += 2;
34299edbd4a0SFrançois Tigeot 
34309edbd4a0SFrançois Tigeot 	/* the declared length is not long enough for the 2 first bytes
34319edbd4a0SFrançois Tigeot 	 * of additional video format capabilities */
34329edbd4a0SFrançois Tigeot 	if (len < (8 + offset + 2))
34339edbd4a0SFrançois Tigeot 		goto out;
34349edbd4a0SFrançois Tigeot 
34359edbd4a0SFrançois Tigeot 	/* 3D_Present */
34369edbd4a0SFrançois Tigeot 	offset++;
34379edbd4a0SFrançois Tigeot 	if (db[8 + offset] & (1 << 7)) {
34389edbd4a0SFrançois Tigeot 		modes += add_hdmi_mandatory_stereo_modes(connector);
34399edbd4a0SFrançois Tigeot 
34409edbd4a0SFrançois Tigeot 		/* 3D_Multi_present */
34419edbd4a0SFrançois Tigeot 		multi_present = (db[8 + offset] & 0x60) >> 5;
34429edbd4a0SFrançois Tigeot 	}
34439edbd4a0SFrançois Tigeot 
34449edbd4a0SFrançois Tigeot 	offset++;
34459edbd4a0SFrançois Tigeot 	vic_len = db[8 + offset] >> 5;
34469edbd4a0SFrançois Tigeot 	hdmi_3d_len = db[8 + offset] & 0x1f;
34479edbd4a0SFrançois Tigeot 
34489edbd4a0SFrançois Tigeot 	for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
34499edbd4a0SFrançois Tigeot 		u8 vic;
34509edbd4a0SFrançois Tigeot 
34519edbd4a0SFrançois Tigeot 		vic = db[9 + offset + i];
34529edbd4a0SFrançois Tigeot 		modes += add_hdmi_mode(connector, vic);
34539edbd4a0SFrançois Tigeot 	}
34549edbd4a0SFrançois Tigeot 	offset += 1 + vic_len;
34559edbd4a0SFrançois Tigeot 
34569edbd4a0SFrançois Tigeot 	if (multi_present == 1)
34579edbd4a0SFrançois Tigeot 		multi_len = 2;
34589edbd4a0SFrançois Tigeot 	else if (multi_present == 2)
34599edbd4a0SFrançois Tigeot 		multi_len = 4;
34609edbd4a0SFrançois Tigeot 	else
34619edbd4a0SFrançois Tigeot 		multi_len = 0;
34629edbd4a0SFrançois Tigeot 
34639edbd4a0SFrançois Tigeot 	if (len < (8 + offset + hdmi_3d_len - 1))
34649edbd4a0SFrançois Tigeot 		goto out;
34659edbd4a0SFrançois Tigeot 
34669edbd4a0SFrançois Tigeot 	if (hdmi_3d_len < multi_len)
34679edbd4a0SFrançois Tigeot 		goto out;
34689edbd4a0SFrançois Tigeot 
34699edbd4a0SFrançois Tigeot 	if (multi_present == 1 || multi_present == 2) {
34709edbd4a0SFrançois Tigeot 		/* 3D_Structure_ALL */
34719edbd4a0SFrançois Tigeot 		structure_all = (db[8 + offset] << 8) | db[9 + offset];
34729edbd4a0SFrançois Tigeot 
34739edbd4a0SFrançois Tigeot 		/* check if 3D_MASK is present */
34749edbd4a0SFrançois Tigeot 		if (multi_present == 2)
34759edbd4a0SFrançois Tigeot 			mask = (db[10 + offset] << 8) | db[11 + offset];
34769edbd4a0SFrançois Tigeot 		else
34779edbd4a0SFrançois Tigeot 			mask = 0xffff;
34789edbd4a0SFrançois Tigeot 
34799edbd4a0SFrançois Tigeot 		for (i = 0; i < 16; i++) {
34809edbd4a0SFrançois Tigeot 			if (mask & (1 << i))
34819edbd4a0SFrançois Tigeot 				modes += add_3d_struct_modes(connector,
34829edbd4a0SFrançois Tigeot 						structure_all,
34839edbd4a0SFrançois Tigeot 						video_db,
34849edbd4a0SFrançois Tigeot 						video_len, i);
34859edbd4a0SFrançois Tigeot 		}
34869edbd4a0SFrançois Tigeot 	}
34879edbd4a0SFrançois Tigeot 
34889edbd4a0SFrançois Tigeot 	offset += multi_len;
34899edbd4a0SFrançois Tigeot 
34909edbd4a0SFrançois Tigeot 	for (i = 0; i < (hdmi_3d_len - multi_len); i++) {
34919edbd4a0SFrançois Tigeot 		int vic_index;
34929edbd4a0SFrançois Tigeot 		struct drm_display_mode *newmode = NULL;
34939edbd4a0SFrançois Tigeot 		unsigned int newflag = 0;
34949edbd4a0SFrançois Tigeot 		bool detail_present;
34959edbd4a0SFrançois Tigeot 
34969edbd4a0SFrançois Tigeot 		detail_present = ((db[8 + offset + i] & 0x0f) > 7);
34979edbd4a0SFrançois Tigeot 
34989edbd4a0SFrançois Tigeot 		if (detail_present && (i + 1 == hdmi_3d_len - multi_len))
34999edbd4a0SFrançois Tigeot 			break;
35009edbd4a0SFrançois Tigeot 
35019edbd4a0SFrançois Tigeot 		/* 2D_VIC_order_X */
35029edbd4a0SFrançois Tigeot 		vic_index = db[8 + offset + i] >> 4;
35039edbd4a0SFrançois Tigeot 
35049edbd4a0SFrançois Tigeot 		/* 3D_Structure_X */
35059edbd4a0SFrançois Tigeot 		switch (db[8 + offset + i] & 0x0f) {
35069edbd4a0SFrançois Tigeot 		case 0:
35079edbd4a0SFrançois Tigeot 			newflag = DRM_MODE_FLAG_3D_FRAME_PACKING;
35089edbd4a0SFrançois Tigeot 			break;
35099edbd4a0SFrançois Tigeot 		case 6:
35109edbd4a0SFrançois Tigeot 			newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
35119edbd4a0SFrançois Tigeot 			break;
35129edbd4a0SFrançois Tigeot 		case 8:
35139edbd4a0SFrançois Tigeot 			/* 3D_Detail_X */
35149edbd4a0SFrançois Tigeot 			if ((db[9 + offset + i] >> 4) == 1)
35159edbd4a0SFrançois Tigeot 				newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
35169edbd4a0SFrançois Tigeot 			break;
35179edbd4a0SFrançois Tigeot 		}
35189edbd4a0SFrançois Tigeot 
35199edbd4a0SFrançois Tigeot 		if (newflag != 0) {
35209edbd4a0SFrançois Tigeot 			newmode = drm_display_mode_from_vic_index(connector,
35219edbd4a0SFrançois Tigeot 								  video_db,
35229edbd4a0SFrançois Tigeot 								  video_len,
35239edbd4a0SFrançois Tigeot 								  vic_index);
35249edbd4a0SFrançois Tigeot 
35259edbd4a0SFrançois Tigeot 			if (newmode) {
35269edbd4a0SFrançois Tigeot 				newmode->flags |= newflag;
35279edbd4a0SFrançois Tigeot 				drm_mode_probed_add(connector, newmode);
35289edbd4a0SFrançois Tigeot 				modes++;
35299edbd4a0SFrançois Tigeot 			}
35309edbd4a0SFrançois Tigeot 		}
35319edbd4a0SFrançois Tigeot 
35329edbd4a0SFrançois Tigeot 		if (detail_present)
35339edbd4a0SFrançois Tigeot 			i++;
35349edbd4a0SFrançois Tigeot 	}
35359edbd4a0SFrançois Tigeot 
35369edbd4a0SFrançois Tigeot out:
35379edbd4a0SFrançois Tigeot 	return modes;
35389edbd4a0SFrançois Tigeot }
35399edbd4a0SFrançois Tigeot 
35406e29dde8SFrançois Tigeot static int
cea_db_payload_len(const u8 * db)35416e29dde8SFrançois Tigeot cea_db_payload_len(const u8 *db)
35426e29dde8SFrançois Tigeot {
35436e29dde8SFrançois Tigeot 	return db[0] & 0x1f;
35446e29dde8SFrançois Tigeot }
35456e29dde8SFrançois Tigeot 
35466e29dde8SFrançois Tigeot static int
cea_db_extended_tag(const u8 * db)3547*3f2dd94aSFrançois Tigeot cea_db_extended_tag(const u8 *db)
3548*3f2dd94aSFrançois Tigeot {
3549*3f2dd94aSFrançois Tigeot 	return db[1];
3550*3f2dd94aSFrançois Tigeot }
3551*3f2dd94aSFrançois Tigeot 
3552*3f2dd94aSFrançois Tigeot static int
cea_db_tag(const u8 * db)35536e29dde8SFrançois Tigeot cea_db_tag(const u8 *db)
35546e29dde8SFrançois Tigeot {
35556e29dde8SFrançois Tigeot 	return db[0] >> 5;
35566e29dde8SFrançois Tigeot }
35576e29dde8SFrançois Tigeot 
35586e29dde8SFrançois Tigeot static int
cea_revision(const u8 * cea)35596e29dde8SFrançois Tigeot cea_revision(const u8 *cea)
35606e29dde8SFrançois Tigeot {
35616e29dde8SFrançois Tigeot 	return cea[1];
35626e29dde8SFrançois Tigeot }
35636e29dde8SFrançois Tigeot 
35646e29dde8SFrançois Tigeot static int
cea_db_offsets(const u8 * cea,int * start,int * end)35656e29dde8SFrançois Tigeot cea_db_offsets(const u8 *cea, int *start, int *end)
35666e29dde8SFrançois Tigeot {
35676e29dde8SFrançois Tigeot 	/* Data block offset in CEA extension block */
35686e29dde8SFrançois Tigeot 	*start = 4;
35696e29dde8SFrançois Tigeot 	*end = cea[2];
35706e29dde8SFrançois Tigeot 	if (*end == 0)
35716e29dde8SFrançois Tigeot 		*end = 127;
35726e29dde8SFrançois Tigeot 	if (*end < 4 || *end > 127)
35736e29dde8SFrançois Tigeot 		return -ERANGE;
35746e29dde8SFrançois Tigeot 	return 0;
35756e29dde8SFrançois Tigeot }
35766e29dde8SFrançois Tigeot 
cea_db_is_hdmi_vsdb(const u8 * db)35779edbd4a0SFrançois Tigeot static bool cea_db_is_hdmi_vsdb(const u8 *db)
35789edbd4a0SFrançois Tigeot {
35799edbd4a0SFrançois Tigeot 	int hdmi_id;
35809edbd4a0SFrançois Tigeot 
35819edbd4a0SFrançois Tigeot 	if (cea_db_tag(db) != VENDOR_BLOCK)
35829edbd4a0SFrançois Tigeot 		return false;
35839edbd4a0SFrançois Tigeot 
35849edbd4a0SFrançois Tigeot 	if (cea_db_payload_len(db) < 5)
35859edbd4a0SFrançois Tigeot 		return false;
35869edbd4a0SFrançois Tigeot 
35879edbd4a0SFrançois Tigeot 	hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
35889edbd4a0SFrançois Tigeot 
35899edbd4a0SFrançois Tigeot 	return hdmi_id == HDMI_IEEE_OUI;
35909edbd4a0SFrançois Tigeot }
35919edbd4a0SFrançois Tigeot 
cea_db_is_hdmi_forum_vsdb(const u8 * db)3592a85cb24fSFrançois Tigeot static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
3593a85cb24fSFrançois Tigeot {
3594a85cb24fSFrançois Tigeot 	unsigned int oui;
3595a85cb24fSFrançois Tigeot 
3596a85cb24fSFrançois Tigeot 	if (cea_db_tag(db) != VENDOR_BLOCK)
3597a85cb24fSFrançois Tigeot 		return false;
3598a85cb24fSFrançois Tigeot 
3599a85cb24fSFrançois Tigeot 	if (cea_db_payload_len(db) < 7)
3600a85cb24fSFrançois Tigeot 		return false;
3601a85cb24fSFrançois Tigeot 
3602a85cb24fSFrançois Tigeot 	oui = db[3] << 16 | db[2] << 8 | db[1];
3603a85cb24fSFrançois Tigeot 
3604a85cb24fSFrançois Tigeot 	return oui == HDMI_FORUM_IEEE_OUI;
3605a85cb24fSFrançois Tigeot }
3606a85cb24fSFrançois Tigeot 
cea_db_is_y420cmdb(const u8 * db)3607*3f2dd94aSFrançois Tigeot static bool cea_db_is_y420cmdb(const u8 *db)
3608*3f2dd94aSFrançois Tigeot {
3609*3f2dd94aSFrançois Tigeot 	if (cea_db_tag(db) != USE_EXTENDED_TAG)
3610*3f2dd94aSFrançois Tigeot 		return false;
3611*3f2dd94aSFrançois Tigeot 
3612*3f2dd94aSFrançois Tigeot 	if (!cea_db_payload_len(db))
3613*3f2dd94aSFrançois Tigeot 		return false;
3614*3f2dd94aSFrançois Tigeot 
3615*3f2dd94aSFrançois Tigeot 	if (cea_db_extended_tag(db) != EXT_VIDEO_CAP_BLOCK_Y420CMDB)
3616*3f2dd94aSFrançois Tigeot 		return false;
3617*3f2dd94aSFrançois Tigeot 
3618*3f2dd94aSFrançois Tigeot 	return true;
3619*3f2dd94aSFrançois Tigeot }
3620*3f2dd94aSFrançois Tigeot 
cea_db_is_y420vdb(const u8 * db)3621*3f2dd94aSFrançois Tigeot static bool cea_db_is_y420vdb(const u8 *db)
3622*3f2dd94aSFrançois Tigeot {
3623*3f2dd94aSFrançois Tigeot 	if (cea_db_tag(db) != USE_EXTENDED_TAG)
3624*3f2dd94aSFrançois Tigeot 		return false;
3625*3f2dd94aSFrançois Tigeot 
3626*3f2dd94aSFrançois Tigeot 	if (!cea_db_payload_len(db))
3627*3f2dd94aSFrançois Tigeot 		return false;
3628*3f2dd94aSFrançois Tigeot 
3629*3f2dd94aSFrançois Tigeot 	if (cea_db_extended_tag(db) != EXT_VIDEO_DATA_BLOCK_420)
3630*3f2dd94aSFrançois Tigeot 		return false;
3631*3f2dd94aSFrançois Tigeot 
3632*3f2dd94aSFrançois Tigeot 	return true;
3633*3f2dd94aSFrançois Tigeot }
3634*3f2dd94aSFrançois Tigeot 
36356e29dde8SFrançois Tigeot #define for_each_cea_db(cea, i, start, end) \
36366e29dde8SFrançois Tigeot 	for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
36376e29dde8SFrançois Tigeot 
drm_parse_y420cmdb_bitmap(struct drm_connector * connector,const u8 * db)3638*3f2dd94aSFrançois Tigeot static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
3639*3f2dd94aSFrançois Tigeot 				      const u8 *db)
3640*3f2dd94aSFrançois Tigeot {
3641*3f2dd94aSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
3642*3f2dd94aSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &info->hdmi;
3643*3f2dd94aSFrançois Tigeot 	u8 map_len = cea_db_payload_len(db) - 1;
3644*3f2dd94aSFrançois Tigeot 	u8 count;
3645*3f2dd94aSFrançois Tigeot 	u64 map = 0;
3646*3f2dd94aSFrançois Tigeot 
3647*3f2dd94aSFrançois Tigeot 	if (map_len == 0) {
3648*3f2dd94aSFrançois Tigeot 		/* All CEA modes support ycbcr420 sampling also.*/
3649*3f2dd94aSFrançois Tigeot 		hdmi->y420_cmdb_map = U64_MAX;
3650*3f2dd94aSFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
3651*3f2dd94aSFrançois Tigeot 		return;
3652*3f2dd94aSFrançois Tigeot 	}
3653*3f2dd94aSFrançois Tigeot 
3654*3f2dd94aSFrançois Tigeot 	/*
3655*3f2dd94aSFrançois Tigeot 	 * This map indicates which of the existing CEA block modes
3656*3f2dd94aSFrançois Tigeot 	 * from VDB can support YCBCR420 output too. So if bit=0 is
3657*3f2dd94aSFrançois Tigeot 	 * set, first mode from VDB can support YCBCR420 output too.
3658*3f2dd94aSFrançois Tigeot 	 * We will parse and keep this map, before parsing VDB itself
3659*3f2dd94aSFrançois Tigeot 	 * to avoid going through the same block again and again.
3660*3f2dd94aSFrançois Tigeot 	 *
3661*3f2dd94aSFrançois Tigeot 	 * Spec is not clear about max possible size of this block.
3662*3f2dd94aSFrançois Tigeot 	 * Clamping max bitmap block size at 8 bytes. Every byte can
3663*3f2dd94aSFrançois Tigeot 	 * address 8 CEA modes, in this way this map can address
3664*3f2dd94aSFrançois Tigeot 	 * 8*8 = first 64 SVDs.
3665*3f2dd94aSFrançois Tigeot 	 */
3666*3f2dd94aSFrançois Tigeot 	if (WARN_ON_ONCE(map_len > 8))
3667*3f2dd94aSFrançois Tigeot 		map_len = 8;
3668*3f2dd94aSFrançois Tigeot 
3669*3f2dd94aSFrançois Tigeot 	for (count = 0; count < map_len; count++)
3670*3f2dd94aSFrançois Tigeot 		map |= (u64)db[2 + count] << (8 * count);
3671*3f2dd94aSFrançois Tigeot 
3672*3f2dd94aSFrançois Tigeot 	if (map)
3673*3f2dd94aSFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
3674*3f2dd94aSFrançois Tigeot 
3675*3f2dd94aSFrançois Tigeot 	hdmi->y420_cmdb_map = map;
3676*3f2dd94aSFrançois Tigeot }
3677*3f2dd94aSFrançois Tigeot 
36786e29dde8SFrançois Tigeot static int
add_cea_modes(struct drm_connector * connector,struct edid * edid)36796e29dde8SFrançois Tigeot add_cea_modes(struct drm_connector *connector, struct edid *edid)
36806e29dde8SFrançois Tigeot {
36819edbd4a0SFrançois Tigeot 	const u8 *cea = drm_find_cea_extension(edid);
36829edbd4a0SFrançois Tigeot 	const u8 *db, *hdmi = NULL, *video = NULL;
36839edbd4a0SFrançois Tigeot 	u8 dbl, hdmi_len, video_len = 0;
36846e29dde8SFrançois Tigeot 	int modes = 0;
36856e29dde8SFrançois Tigeot 
36866e29dde8SFrançois Tigeot 	if (cea && cea_revision(cea) >= 3) {
36876e29dde8SFrançois Tigeot 		int i, start, end;
36886e29dde8SFrançois Tigeot 
36896e29dde8SFrançois Tigeot 		if (cea_db_offsets(cea, &start, &end))
36906e29dde8SFrançois Tigeot 			return 0;
36916e29dde8SFrançois Tigeot 
36926e29dde8SFrançois Tigeot 		for_each_cea_db(cea, i, start, end) {
36936e29dde8SFrançois Tigeot 			db = &cea[i];
36946e29dde8SFrançois Tigeot 			dbl = cea_db_payload_len(db);
36956e29dde8SFrançois Tigeot 
36969edbd4a0SFrançois Tigeot 			if (cea_db_tag(db) == VIDEO_BLOCK) {
36979edbd4a0SFrançois Tigeot 				video = db + 1;
36989edbd4a0SFrançois Tigeot 				video_len = dbl;
36999edbd4a0SFrançois Tigeot 				modes += do_cea_modes(connector, video, dbl);
3700*3f2dd94aSFrançois Tigeot 			} else if (cea_db_is_hdmi_vsdb(db)) {
37019edbd4a0SFrançois Tigeot 				hdmi = db;
37029edbd4a0SFrançois Tigeot 				hdmi_len = dbl;
3703*3f2dd94aSFrançois Tigeot 			} else if (cea_db_is_y420vdb(db)) {
3704*3f2dd94aSFrançois Tigeot 				const u8 *vdb420 = &db[2];
3705*3f2dd94aSFrançois Tigeot 
3706*3f2dd94aSFrançois Tigeot 				/* Add 4:2:0(only) modes present in EDID */
3707*3f2dd94aSFrançois Tigeot 				modes += do_y420vdb_modes(connector,
3708*3f2dd94aSFrançois Tigeot 							  vdb420,
3709*3f2dd94aSFrançois Tigeot 							  dbl - 1);
37106e29dde8SFrançois Tigeot 			}
37116e29dde8SFrançois Tigeot 		}
37129edbd4a0SFrançois Tigeot 	}
37139edbd4a0SFrançois Tigeot 
37149edbd4a0SFrançois Tigeot 	/*
37159edbd4a0SFrançois Tigeot 	 * We parse the HDMI VSDB after having added the cea modes as we will
37169edbd4a0SFrançois Tigeot 	 * be patching their flags when the sink supports stereo 3D.
37179edbd4a0SFrançois Tigeot 	 */
37189edbd4a0SFrançois Tigeot 	if (hdmi)
37199edbd4a0SFrançois Tigeot 		modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
37209edbd4a0SFrançois Tigeot 					    video_len);
37216e29dde8SFrançois Tigeot 
37226e29dde8SFrançois Tigeot 	return modes;
37236e29dde8SFrançois Tigeot }
37246e29dde8SFrançois Tigeot 
fixup_detailed_cea_mode_clock(struct drm_display_mode * mode)3725352ff8bdSFrançois Tigeot static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
3726352ff8bdSFrançois Tigeot {
3727352ff8bdSFrançois Tigeot 	const struct drm_display_mode *cea_mode;
3728352ff8bdSFrançois Tigeot 	int clock1, clock2, clock;
3729aee94f86SFrançois Tigeot 	u8 vic;
3730352ff8bdSFrançois Tigeot 	const char *type;
3731352ff8bdSFrançois Tigeot 
3732aee94f86SFrançois Tigeot 	/*
3733aee94f86SFrançois Tigeot 	 * allow 5kHz clock difference either way to account for
3734aee94f86SFrançois Tigeot 	 * the 10kHz clock resolution limit of detailed timings.
3735aee94f86SFrançois Tigeot 	 */
3736aee94f86SFrançois Tigeot 	vic = drm_match_cea_mode_clock_tolerance(mode, 5);
3737aee94f86SFrançois Tigeot 	if (drm_valid_cea_vic(vic)) {
3738352ff8bdSFrançois Tigeot 		type = "CEA";
3739aee94f86SFrançois Tigeot 		cea_mode = &edid_cea_modes[vic];
3740352ff8bdSFrançois Tigeot 		clock1 = cea_mode->clock;
3741352ff8bdSFrançois Tigeot 		clock2 = cea_mode_alternate_clock(cea_mode);
3742352ff8bdSFrançois Tigeot 	} else {
3743aee94f86SFrançois Tigeot 		vic = drm_match_hdmi_mode_clock_tolerance(mode, 5);
3744aee94f86SFrançois Tigeot 		if (drm_valid_hdmi_vic(vic)) {
3745352ff8bdSFrançois Tigeot 			type = "HDMI";
3746aee94f86SFrançois Tigeot 			cea_mode = &edid_4k_modes[vic];
3747352ff8bdSFrançois Tigeot 			clock1 = cea_mode->clock;
3748352ff8bdSFrançois Tigeot 			clock2 = hdmi_mode_alternate_clock(cea_mode);
3749352ff8bdSFrançois Tigeot 		} else {
3750352ff8bdSFrançois Tigeot 			return;
3751352ff8bdSFrançois Tigeot 		}
3752352ff8bdSFrançois Tigeot 	}
3753352ff8bdSFrançois Tigeot 
3754352ff8bdSFrançois Tigeot 	/* pick whichever is closest */
3755352ff8bdSFrançois Tigeot 	if (abs(mode->clock - clock1) < abs(mode->clock - clock2))
3756352ff8bdSFrançois Tigeot 		clock = clock1;
3757352ff8bdSFrançois Tigeot 	else
3758352ff8bdSFrançois Tigeot 		clock = clock2;
3759352ff8bdSFrançois Tigeot 
3760352ff8bdSFrançois Tigeot 	if (mode->clock == clock)
3761352ff8bdSFrançois Tigeot 		return;
3762352ff8bdSFrançois Tigeot 
3763352ff8bdSFrançois Tigeot 	DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
3764aee94f86SFrançois Tigeot 		  type, vic, mode->clock, clock);
3765352ff8bdSFrançois Tigeot 	mode->clock = clock;
3766352ff8bdSFrançois Tigeot }
3767352ff8bdSFrançois Tigeot 
37686e29dde8SFrançois Tigeot static void
drm_parse_hdmi_vsdb_audio(struct drm_connector * connector,const u8 * db)37691dedbd3bSFrançois Tigeot drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
37706e29dde8SFrançois Tigeot {
37716e29dde8SFrançois Tigeot 	u8 len = cea_db_payload_len(db);
37726e29dde8SFrançois Tigeot 
37731dedbd3bSFrançois Tigeot 	if (len >= 6)
37745718399fSFrançois Tigeot 		connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
37756e29dde8SFrançois Tigeot 	if (len >= 8) {
37765718399fSFrançois Tigeot 		connector->latency_present[0] = db[8] >> 7;
37775718399fSFrançois Tigeot 		connector->latency_present[1] = (db[8] >> 6) & 1;
37786e29dde8SFrançois Tigeot 	}
37796e29dde8SFrançois Tigeot 	if (len >= 9)
37805718399fSFrançois Tigeot 		connector->video_latency[0] = db[9];
37816e29dde8SFrançois Tigeot 	if (len >= 10)
37825718399fSFrançois Tigeot 		connector->audio_latency[0] = db[10];
37836e29dde8SFrançois Tigeot 	if (len >= 11)
37845718399fSFrançois Tigeot 		connector->video_latency[1] = db[11];
37856e29dde8SFrançois Tigeot 	if (len >= 12)
37865718399fSFrançois Tigeot 		connector->audio_latency[1] = db[12];
37875718399fSFrançois Tigeot 
37881dedbd3bSFrançois Tigeot 	DRM_DEBUG_KMS("HDMI: latency present %d %d, "
37895718399fSFrançois Tigeot 		      "video latency %d %d, "
37905718399fSFrançois Tigeot 		      "audio latency %d %d\n",
37911dedbd3bSFrançois Tigeot 		      connector->latency_present[0],
37921dedbd3bSFrançois Tigeot 		      connector->latency_present[1],
37935718399fSFrançois Tigeot 		      connector->video_latency[0],
37945718399fSFrançois Tigeot 		      connector->video_latency[1],
37955718399fSFrançois Tigeot 		      connector->audio_latency[0],
37965718399fSFrançois Tigeot 		      connector->audio_latency[1]);
37975718399fSFrançois Tigeot }
37985718399fSFrançois Tigeot 
37995718399fSFrançois Tigeot static void
monitor_name(struct detailed_timing * t,void * data)38005718399fSFrançois Tigeot monitor_name(struct detailed_timing *t, void *data)
38015718399fSFrançois Tigeot {
38025718399fSFrançois Tigeot 	if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
38035718399fSFrançois Tigeot 		*(u8 **)data = t->data.other_data.data.str.str;
38045718399fSFrançois Tigeot }
38055718399fSFrançois Tigeot 
get_monitor_name(struct edid * edid,char name[13])38068621f407SFrançois Tigeot static int get_monitor_name(struct edid *edid, char name[13])
38078621f407SFrançois Tigeot {
38088621f407SFrançois Tigeot 	char *edid_name = NULL;
38098621f407SFrançois Tigeot 	int mnl;
38108621f407SFrançois Tigeot 
38118621f407SFrançois Tigeot 	if (!edid || !name)
38128621f407SFrançois Tigeot 		return 0;
38138621f407SFrançois Tigeot 
38148621f407SFrançois Tigeot 	drm_for_each_detailed_block((u8 *)edid, monitor_name, &edid_name);
38158621f407SFrançois Tigeot 	for (mnl = 0; edid_name && mnl < 13; mnl++) {
38168621f407SFrançois Tigeot 		if (edid_name[mnl] == 0x0a)
38178621f407SFrançois Tigeot 			break;
38188621f407SFrançois Tigeot 
38198621f407SFrançois Tigeot 		name[mnl] = edid_name[mnl];
38208621f407SFrançois Tigeot 	}
38218621f407SFrançois Tigeot 
38228621f407SFrançois Tigeot 	return mnl;
38238621f407SFrançois Tigeot }
38248621f407SFrançois Tigeot 
38258621f407SFrançois Tigeot /**
38268621f407SFrançois Tigeot  * drm_edid_get_monitor_name - fetch the monitor name from the edid
38278621f407SFrançois Tigeot  * @edid: monitor EDID information
38288621f407SFrançois Tigeot  * @name: pointer to a character array to hold the name of the monitor
38298621f407SFrançois Tigeot  * @bufsize: The size of the name buffer (should be at least 14 chars.)
38308621f407SFrançois Tigeot  *
38318621f407SFrançois Tigeot  */
drm_edid_get_monitor_name(struct edid * edid,char * name,int bufsize)38328621f407SFrançois Tigeot void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize)
38338621f407SFrançois Tigeot {
38348621f407SFrançois Tigeot 	int name_length;
38358621f407SFrançois Tigeot 	char buf[13];
38368621f407SFrançois Tigeot 
38378621f407SFrançois Tigeot 	if (bufsize <= 0)
38388621f407SFrançois Tigeot 		return;
38398621f407SFrançois Tigeot 
38408621f407SFrançois Tigeot 	name_length = min(get_monitor_name(edid, buf), bufsize - 1);
38418621f407SFrançois Tigeot 	memcpy(name, buf, name_length);
38428621f407SFrançois Tigeot 	name[name_length] = '\0';
38438621f407SFrançois Tigeot }
38448621f407SFrançois Tigeot EXPORT_SYMBOL(drm_edid_get_monitor_name);
38458621f407SFrançois Tigeot 
38465718399fSFrançois Tigeot /**
38475718399fSFrançois Tigeot  * drm_edid_to_eld - build ELD from EDID
38485718399fSFrançois Tigeot  * @connector: connector corresponding to the HDMI/DP sink
38495718399fSFrançois Tigeot  * @edid: EDID to parse
38505718399fSFrançois Tigeot  *
3851ba55f2f5SFrançois Tigeot  * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
3852*3f2dd94aSFrançois Tigeot  * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
38535718399fSFrançois Tigeot  */
drm_edid_to_eld(struct drm_connector * connector,struct edid * edid)38545718399fSFrançois Tigeot void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
38555718399fSFrançois Tigeot {
38565718399fSFrançois Tigeot 	uint8_t *eld = connector->eld;
38575718399fSFrançois Tigeot 	u8 *cea;
38585718399fSFrançois Tigeot 	u8 *db;
3859c0e85e96SFrançois Tigeot 	int total_sad_count = 0;
38605718399fSFrançois Tigeot 	int mnl;
38615718399fSFrançois Tigeot 	int dbl;
38625718399fSFrançois Tigeot 
38635718399fSFrançois Tigeot 	memset(eld, 0, sizeof(connector->eld));
38645718399fSFrançois Tigeot 
38651dedbd3bSFrançois Tigeot 	connector->latency_present[0] = false;
38661dedbd3bSFrançois Tigeot 	connector->latency_present[1] = false;
38671dedbd3bSFrançois Tigeot 	connector->video_latency[0] = 0;
38681dedbd3bSFrançois Tigeot 	connector->audio_latency[0] = 0;
38691dedbd3bSFrançois Tigeot 	connector->video_latency[1] = 0;
38701dedbd3bSFrançois Tigeot 	connector->audio_latency[1] = 0;
38711dedbd3bSFrançois Tigeot 
3872a85cb24fSFrançois Tigeot 	if (!edid)
3873a85cb24fSFrançois Tigeot 		return;
3874a85cb24fSFrançois Tigeot 
38755718399fSFrançois Tigeot 	cea = drm_find_cea_extension(edid);
38765718399fSFrançois Tigeot 	if (!cea) {
38775718399fSFrançois Tigeot 		DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
38785718399fSFrançois Tigeot 		return;
38795718399fSFrançois Tigeot 	}
38805718399fSFrançois Tigeot 
38818621f407SFrançois Tigeot 	mnl = get_monitor_name(edid, eld + 20);
38828621f407SFrançois Tigeot 
38835718399fSFrançois Tigeot 	eld[4] = (cea[1] << 5) | mnl;
38845718399fSFrançois Tigeot 	DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
38855718399fSFrançois Tigeot 
38865718399fSFrançois Tigeot 	eld[0] = 2 << 3;		/* ELD version: 2 */
38875718399fSFrançois Tigeot 
38885718399fSFrançois Tigeot 	eld[16] = edid->mfg_id[0];
38895718399fSFrançois Tigeot 	eld[17] = edid->mfg_id[1];
38905718399fSFrançois Tigeot 	eld[18] = edid->prod_code[0];
38915718399fSFrançois Tigeot 	eld[19] = edid->prod_code[1];
38925718399fSFrançois Tigeot 
38936e29dde8SFrançois Tigeot 	if (cea_revision(cea) >= 3) {
38946e29dde8SFrançois Tigeot 		int i, start, end;
38955718399fSFrançois Tigeot 
38966e29dde8SFrançois Tigeot 		if (cea_db_offsets(cea, &start, &end)) {
38976e29dde8SFrançois Tigeot 			start = 0;
38986e29dde8SFrançois Tigeot 			end = 0;
38996e29dde8SFrançois Tigeot 		}
39006e29dde8SFrançois Tigeot 
39016e29dde8SFrançois Tigeot 		for_each_cea_db(cea, i, start, end) {
39026e29dde8SFrançois Tigeot 			db = &cea[i];
39036e29dde8SFrançois Tigeot 			dbl = cea_db_payload_len(db);
39046e29dde8SFrançois Tigeot 
39056e29dde8SFrançois Tigeot 			switch (cea_db_tag(db)) {
3906c0e85e96SFrançois Tigeot 				int sad_count;
3907c0e85e96SFrançois Tigeot 
39086e29dde8SFrançois Tigeot 			case AUDIO_BLOCK:
39096e29dde8SFrançois Tigeot 				/* Audio Data Block, contains SADs */
3910c0e85e96SFrançois Tigeot 				sad_count = min(dbl / 3, 15 - total_sad_count);
3911c0e85e96SFrançois Tigeot 				if (sad_count >= 1)
3912c0e85e96SFrançois Tigeot 					memcpy(eld + 20 + mnl + total_sad_count * 3,
3913c0e85e96SFrançois Tigeot 					       &db[1], sad_count * 3);
3914c0e85e96SFrançois Tigeot 				total_sad_count += sad_count;
39155718399fSFrançois Tigeot 				break;
39166e29dde8SFrançois Tigeot 			case SPEAKER_BLOCK:
39176e29dde8SFrançois Tigeot 				/* Speaker Allocation Data Block */
39186e29dde8SFrançois Tigeot 				if (dbl >= 1)
39195718399fSFrançois Tigeot 					eld[7] = db[1];
39205718399fSFrançois Tigeot 				break;
39215718399fSFrançois Tigeot 			case VENDOR_BLOCK:
39225718399fSFrançois Tigeot 				/* HDMI Vendor-Specific Data Block */
39236e29dde8SFrançois Tigeot 				if (cea_db_is_hdmi_vsdb(db))
39241dedbd3bSFrançois Tigeot 					drm_parse_hdmi_vsdb_audio(connector, db);
39255718399fSFrançois Tigeot 				break;
39265718399fSFrançois Tigeot 			default:
39275718399fSFrançois Tigeot 				break;
39285718399fSFrançois Tigeot 			}
39295718399fSFrançois Tigeot 		}
39306e29dde8SFrançois Tigeot 	}
3931c0e85e96SFrançois Tigeot 	eld[5] |= total_sad_count << 4;
39325718399fSFrançois Tigeot 
3933*3f2dd94aSFrançois Tigeot 	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
3934*3f2dd94aSFrançois Tigeot 	    connector->connector_type == DRM_MODE_CONNECTOR_eDP)
3935*3f2dd94aSFrançois Tigeot 		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
3936*3f2dd94aSFrançois Tigeot 	else
3937*3f2dd94aSFrançois Tigeot 		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
3938*3f2dd94aSFrançois Tigeot 
3939c0e85e96SFrançois Tigeot 	eld[DRM_ELD_BASELINE_ELD_LEN] =
3940c0e85e96SFrançois Tigeot 		DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
3941c0e85e96SFrançois Tigeot 
3942c0e85e96SFrançois Tigeot 	DRM_DEBUG_KMS("ELD size %d, SAD count %d\n",
3943c0e85e96SFrançois Tigeot 		      drm_eld_size(eld), total_sad_count);
39445718399fSFrançois Tigeot }
39456e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_eld);
39465718399fSFrançois Tigeot 
39475718399fSFrançois Tigeot /**
39489edbd4a0SFrançois Tigeot  * drm_edid_to_sad - extracts SADs from EDID
39499edbd4a0SFrançois Tigeot  * @edid: EDID to parse
39509edbd4a0SFrançois Tigeot  * @sads: pointer that will be set to the extracted SADs
39519edbd4a0SFrançois Tigeot  *
39529edbd4a0SFrançois Tigeot  * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
39539edbd4a0SFrançois Tigeot  *
3954ba55f2f5SFrançois Tigeot  * Note: The returned pointer needs to be freed using kfree().
3955ba55f2f5SFrançois Tigeot  *
3956ba55f2f5SFrançois Tigeot  * Return: The number of found SADs or negative number on error.
39579edbd4a0SFrançois Tigeot  */
drm_edid_to_sad(struct edid * edid,struct cea_sad ** sads)39589edbd4a0SFrançois Tigeot int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
39599edbd4a0SFrançois Tigeot {
39609edbd4a0SFrançois Tigeot 	int count = 0;
39619edbd4a0SFrançois Tigeot 	int i, start, end, dbl;
39629edbd4a0SFrançois Tigeot 	u8 *cea;
39639edbd4a0SFrançois Tigeot 
39649edbd4a0SFrançois Tigeot 	cea = drm_find_cea_extension(edid);
39659edbd4a0SFrançois Tigeot 	if (!cea) {
39669edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
39679edbd4a0SFrançois Tigeot 		return -ENOENT;
39689edbd4a0SFrançois Tigeot 	}
39699edbd4a0SFrançois Tigeot 
39709edbd4a0SFrançois Tigeot 	if (cea_revision(cea) < 3) {
39719edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
39721e12ee3bSFrançois Tigeot 		return -ENOTSUPP;
39739edbd4a0SFrançois Tigeot 	}
39749edbd4a0SFrançois Tigeot 
39759edbd4a0SFrançois Tigeot 	if (cea_db_offsets(cea, &start, &end)) {
39769edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
39779edbd4a0SFrançois Tigeot 		return -EPROTO;
39789edbd4a0SFrançois Tigeot 	}
39799edbd4a0SFrançois Tigeot 
39809edbd4a0SFrançois Tigeot 	for_each_cea_db(cea, i, start, end) {
39819edbd4a0SFrançois Tigeot 		u8 *db = &cea[i];
39829edbd4a0SFrançois Tigeot 
39839edbd4a0SFrançois Tigeot 		if (cea_db_tag(db) == AUDIO_BLOCK) {
39849edbd4a0SFrançois Tigeot 			int j;
39859edbd4a0SFrançois Tigeot 			dbl = cea_db_payload_len(db);
39869edbd4a0SFrançois Tigeot 
39879edbd4a0SFrançois Tigeot 			count = dbl / 3; /* SAD is 3B */
398822ee5efbSFrançois Tigeot 			*sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
39899edbd4a0SFrançois Tigeot 			if (!*sads)
39909edbd4a0SFrançois Tigeot 				return -ENOMEM;
39919edbd4a0SFrançois Tigeot 			for (j = 0; j < count; j++) {
39929edbd4a0SFrançois Tigeot 				u8 *sad = &db[1 + j * 3];
39939edbd4a0SFrançois Tigeot 
39949edbd4a0SFrançois Tigeot 				(*sads)[j].format = (sad[0] & 0x78) >> 3;
39959edbd4a0SFrançois Tigeot 				(*sads)[j].channels = sad[0] & 0x7;
39969edbd4a0SFrançois Tigeot 				(*sads)[j].freq = sad[1] & 0x7F;
39979edbd4a0SFrançois Tigeot 				(*sads)[j].byte2 = sad[2];
39989edbd4a0SFrançois Tigeot 			}
39999edbd4a0SFrançois Tigeot 			break;
40009edbd4a0SFrançois Tigeot 		}
40019edbd4a0SFrançois Tigeot 	}
40029edbd4a0SFrançois Tigeot 
40039edbd4a0SFrançois Tigeot 	return count;
40049edbd4a0SFrançois Tigeot }
40059edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_sad);
40069edbd4a0SFrançois Tigeot 
40079edbd4a0SFrançois Tigeot /**
40089edbd4a0SFrançois Tigeot  * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
40099edbd4a0SFrançois Tigeot  * @edid: EDID to parse
40109edbd4a0SFrançois Tigeot  * @sadb: pointer to the speaker block
40119edbd4a0SFrançois Tigeot  *
40129edbd4a0SFrançois Tigeot  * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
40139edbd4a0SFrançois Tigeot  *
4014ba55f2f5SFrançois Tigeot  * Note: The returned pointer needs to be freed using kfree().
4015ba55f2f5SFrançois Tigeot  *
4016ba55f2f5SFrançois Tigeot  * Return: The number of found Speaker Allocation Blocks or negative number on
4017ba55f2f5SFrançois Tigeot  * error.
40189edbd4a0SFrançois Tigeot  */
drm_edid_to_speaker_allocation(struct edid * edid,u8 ** sadb)40199edbd4a0SFrançois Tigeot int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
40209edbd4a0SFrançois Tigeot {
40219edbd4a0SFrançois Tigeot 	int count = 0;
40229edbd4a0SFrançois Tigeot 	int i, start, end, dbl;
40239edbd4a0SFrançois Tigeot 	const u8 *cea;
40249edbd4a0SFrançois Tigeot 
40259edbd4a0SFrançois Tigeot 	cea = drm_find_cea_extension(edid);
40269edbd4a0SFrançois Tigeot 	if (!cea) {
40279edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
40289edbd4a0SFrançois Tigeot 		return -ENOENT;
40299edbd4a0SFrançois Tigeot 	}
40309edbd4a0SFrançois Tigeot 
40319edbd4a0SFrançois Tigeot 	if (cea_revision(cea) < 3) {
40329edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
40339edbd4a0SFrançois Tigeot 		return -ENOTSUPP;
40349edbd4a0SFrançois Tigeot 	}
40359edbd4a0SFrançois Tigeot 
40369edbd4a0SFrançois Tigeot 	if (cea_db_offsets(cea, &start, &end)) {
40379edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
40389edbd4a0SFrançois Tigeot 		return -EPROTO;
40399edbd4a0SFrançois Tigeot 	}
40409edbd4a0SFrançois Tigeot 
40419edbd4a0SFrançois Tigeot 	for_each_cea_db(cea, i, start, end) {
40429edbd4a0SFrançois Tigeot 		const u8 *db = &cea[i];
40439edbd4a0SFrançois Tigeot 
40449edbd4a0SFrançois Tigeot 		if (cea_db_tag(db) == SPEAKER_BLOCK) {
40459edbd4a0SFrançois Tigeot 			dbl = cea_db_payload_len(db);
40469edbd4a0SFrançois Tigeot 
40479edbd4a0SFrançois Tigeot 			/* Speaker Allocation Data Block */
40489edbd4a0SFrançois Tigeot 			if (dbl == 3) {
4049ba55f2f5SFrançois Tigeot 				*sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
4050ba55f2f5SFrançois Tigeot 				if (!*sadb)
4051ba55f2f5SFrançois Tigeot 					return -ENOMEM;
40529edbd4a0SFrançois Tigeot 				count = dbl;
40539edbd4a0SFrançois Tigeot 				break;
40549edbd4a0SFrançois Tigeot 			}
40559edbd4a0SFrançois Tigeot 		}
40569edbd4a0SFrançois Tigeot 	}
40579edbd4a0SFrançois Tigeot 
40589edbd4a0SFrançois Tigeot 	return count;
40599edbd4a0SFrançois Tigeot }
40609edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
40619edbd4a0SFrançois Tigeot 
40629edbd4a0SFrançois Tigeot /**
4063ba55f2f5SFrançois Tigeot  * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
40645718399fSFrançois Tigeot  * @connector: connector associated with the HDMI/DP sink
40655718399fSFrançois Tigeot  * @mode: the display mode
4066ba55f2f5SFrançois Tigeot  *
4067ba55f2f5SFrançois Tigeot  * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
4068ba55f2f5SFrançois Tigeot  * the sink doesn't support audio or video.
40695718399fSFrançois Tigeot  */
drm_av_sync_delay(struct drm_connector * connector,const struct drm_display_mode * mode)40705718399fSFrançois Tigeot int drm_av_sync_delay(struct drm_connector *connector,
4071352ff8bdSFrançois Tigeot 		      const struct drm_display_mode *mode)
40725718399fSFrançois Tigeot {
40735718399fSFrançois Tigeot 	int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
40745718399fSFrançois Tigeot 	int a, v;
40755718399fSFrançois Tigeot 
40765718399fSFrançois Tigeot 	if (!connector->latency_present[0])
40775718399fSFrançois Tigeot 		return 0;
40785718399fSFrançois Tigeot 	if (!connector->latency_present[1])
40795718399fSFrançois Tigeot 		i = 0;
40805718399fSFrançois Tigeot 
40815718399fSFrançois Tigeot 	a = connector->audio_latency[i];
40825718399fSFrançois Tigeot 	v = connector->video_latency[i];
40835718399fSFrançois Tigeot 
40845718399fSFrançois Tigeot 	/*
40855718399fSFrançois Tigeot 	 * HDMI/DP sink doesn't support audio or video?
40865718399fSFrançois Tigeot 	 */
40875718399fSFrançois Tigeot 	if (a == 255 || v == 255)
40885718399fSFrançois Tigeot 		return 0;
40895718399fSFrançois Tigeot 
40905718399fSFrançois Tigeot 	/*
40915718399fSFrançois Tigeot 	 * Convert raw EDID values to millisecond.
40925718399fSFrançois Tigeot 	 * Treat unknown latency as 0ms.
40935718399fSFrançois Tigeot 	 */
40945718399fSFrançois Tigeot 	if (a)
40955718399fSFrançois Tigeot 		a = min(2 * (a - 1), 500);
40965718399fSFrançois Tigeot 	if (v)
40975718399fSFrançois Tigeot 		v = min(2 * (v - 1), 500);
40985718399fSFrançois Tigeot 
40995718399fSFrançois Tigeot 	return max(v - a, 0);
41005718399fSFrançois Tigeot }
41016e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_av_sync_delay);
41025718399fSFrançois Tigeot 
41035718399fSFrançois Tigeot /**
4104ba55f2f5SFrançois Tigeot  * drm_detect_hdmi_monitor - detect whether monitor is HDMI
41055718399fSFrançois Tigeot  * @edid: monitor EDID information
41065718399fSFrançois Tigeot  *
41075718399fSFrançois Tigeot  * Parse the CEA extension according to CEA-861-B.
4108ba55f2f5SFrançois Tigeot  *
4109ba55f2f5SFrançois Tigeot  * Return: True if the monitor is HDMI, false if not or unknown.
41105718399fSFrançois Tigeot  */
drm_detect_hdmi_monitor(struct edid * edid)41115718399fSFrançois Tigeot bool drm_detect_hdmi_monitor(struct edid *edid)
41125718399fSFrançois Tigeot {
41135718399fSFrançois Tigeot 	u8 *edid_ext;
41146e29dde8SFrançois Tigeot 	int i;
41155718399fSFrançois Tigeot 	int start_offset, end_offset;
41165718399fSFrançois Tigeot 
41175718399fSFrançois Tigeot 	edid_ext = drm_find_cea_extension(edid);
41185718399fSFrançois Tigeot 	if (!edid_ext)
41196e29dde8SFrançois Tigeot 		return false;
41205718399fSFrançois Tigeot 
41216e29dde8SFrançois Tigeot 	if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
41226e29dde8SFrançois Tigeot 		return false;
41235718399fSFrançois Tigeot 
41245718399fSFrançois Tigeot 	/*
41255718399fSFrançois Tigeot 	 * Because HDMI identifier is in Vendor Specific Block,
41265718399fSFrançois Tigeot 	 * search it from all data blocks of CEA extension.
41275718399fSFrançois Tigeot 	 */
41286e29dde8SFrançois Tigeot 	for_each_cea_db(edid_ext, i, start_offset, end_offset) {
41296e29dde8SFrançois Tigeot 		if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
41306e29dde8SFrançois Tigeot 			return true;
41315718399fSFrançois Tigeot 	}
41325718399fSFrançois Tigeot 
41336e29dde8SFrançois Tigeot 	return false;
41345718399fSFrançois Tigeot }
41356e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_hdmi_monitor);
41365718399fSFrançois Tigeot 
41375718399fSFrançois Tigeot /**
41385718399fSFrançois Tigeot  * drm_detect_monitor_audio - check monitor audio capability
4139ba55f2f5SFrançois Tigeot  * @edid: EDID block to scan
41405718399fSFrançois Tigeot  *
41415718399fSFrançois Tigeot  * Monitor should have CEA extension block.
41425718399fSFrançois Tigeot  * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
41435718399fSFrançois Tigeot  * audio' only. If there is any audio extension block and supported
41445718399fSFrançois Tigeot  * audio format, assume at least 'basic audio' support, even if 'basic
41455718399fSFrançois Tigeot  * audio' is not defined in EDID.
41465718399fSFrançois Tigeot  *
4147ba55f2f5SFrançois Tigeot  * Return: True if the monitor supports audio, false otherwise.
41485718399fSFrançois Tigeot  */
drm_detect_monitor_audio(struct edid * edid)41495718399fSFrançois Tigeot bool drm_detect_monitor_audio(struct edid *edid)
41505718399fSFrançois Tigeot {
41515718399fSFrançois Tigeot 	u8 *edid_ext;
41525718399fSFrançois Tigeot 	int i, j;
41535718399fSFrançois Tigeot 	bool has_audio = false;
41545718399fSFrançois Tigeot 	int start_offset, end_offset;
41555718399fSFrançois Tigeot 
41565718399fSFrançois Tigeot 	edid_ext = drm_find_cea_extension(edid);
41575718399fSFrançois Tigeot 	if (!edid_ext)
41585718399fSFrançois Tigeot 		goto end;
41595718399fSFrançois Tigeot 
41605718399fSFrançois Tigeot 	has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
41615718399fSFrançois Tigeot 
41625718399fSFrançois Tigeot 	if (has_audio) {
41635718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Monitor has basic audio support\n");
41645718399fSFrançois Tigeot 		goto end;
41655718399fSFrançois Tigeot 	}
41665718399fSFrançois Tigeot 
41676e29dde8SFrançois Tigeot 	if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
41686e29dde8SFrançois Tigeot 		goto end;
41695718399fSFrançois Tigeot 
41706e29dde8SFrançois Tigeot 	for_each_cea_db(edid_ext, i, start_offset, end_offset) {
41716e29dde8SFrançois Tigeot 		if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
41725718399fSFrançois Tigeot 			has_audio = true;
41736e29dde8SFrançois Tigeot 			for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
41745718399fSFrançois Tigeot 				DRM_DEBUG_KMS("CEA audio format %d\n",
41755718399fSFrançois Tigeot 					      (edid_ext[i + j] >> 3) & 0xf);
41765718399fSFrançois Tigeot 			goto end;
41775718399fSFrançois Tigeot 		}
41785718399fSFrançois Tigeot 	}
41795718399fSFrançois Tigeot end:
41805718399fSFrançois Tigeot 	return has_audio;
41815718399fSFrançois Tigeot }
41826e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_detect_monitor_audio);
41835718399fSFrançois Tigeot 
41845718399fSFrançois Tigeot /**
4185a2fdbec6SFrançois Tigeot  * drm_rgb_quant_range_selectable - is RGB quantization range selectable?
4186ba55f2f5SFrançois Tigeot  * @edid: EDID block to scan
4187a2fdbec6SFrançois Tigeot  *
4188a2fdbec6SFrançois Tigeot  * Check whether the monitor reports the RGB quantization range selection
4189a2fdbec6SFrançois Tigeot  * as supported. The AVI infoframe can then be used to inform the monitor
4190a2fdbec6SFrançois Tigeot  * which quantization range (full or limited) is used.
4191ba55f2f5SFrançois Tigeot  *
4192ba55f2f5SFrançois Tigeot  * Return: True if the RGB quantization range is selectable, false otherwise.
4193a2fdbec6SFrançois Tigeot  */
drm_rgb_quant_range_selectable(struct edid * edid)4194a2fdbec6SFrançois Tigeot bool drm_rgb_quant_range_selectable(struct edid *edid)
4195a2fdbec6SFrançois Tigeot {
4196a2fdbec6SFrançois Tigeot 	u8 *edid_ext;
4197a2fdbec6SFrançois Tigeot 	int i, start, end;
4198a2fdbec6SFrançois Tigeot 
4199a2fdbec6SFrançois Tigeot 	edid_ext = drm_find_cea_extension(edid);
4200a2fdbec6SFrançois Tigeot 	if (!edid_ext)
4201a2fdbec6SFrançois Tigeot 		return false;
4202a2fdbec6SFrançois Tigeot 
4203a2fdbec6SFrançois Tigeot 	if (cea_db_offsets(edid_ext, &start, &end))
4204a2fdbec6SFrançois Tigeot 		return false;
4205a2fdbec6SFrançois Tigeot 
4206a2fdbec6SFrançois Tigeot 	for_each_cea_db(edid_ext, i, start, end) {
4207*3f2dd94aSFrançois Tigeot 		if (cea_db_tag(&edid_ext[i]) == USE_EXTENDED_TAG &&
4208*3f2dd94aSFrançois Tigeot 		    cea_db_payload_len(&edid_ext[i]) == 2 &&
4209*3f2dd94aSFrançois Tigeot 		    cea_db_extended_tag(&edid_ext[i]) ==
4210*3f2dd94aSFrançois Tigeot 			EXT_VIDEO_CAPABILITY_BLOCK) {
4211a2fdbec6SFrançois Tigeot 			DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]);
4212a2fdbec6SFrançois Tigeot 			return edid_ext[i + 2] & EDID_CEA_VCDB_QS;
4213a2fdbec6SFrançois Tigeot 		}
4214a2fdbec6SFrançois Tigeot 	}
4215a2fdbec6SFrançois Tigeot 
4216a2fdbec6SFrançois Tigeot 	return false;
4217a2fdbec6SFrançois Tigeot }
4218a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
4219a2fdbec6SFrançois Tigeot 
4220a85cb24fSFrançois Tigeot /**
4221a85cb24fSFrançois Tigeot  * drm_default_rgb_quant_range - default RGB quantization range
4222a85cb24fSFrançois Tigeot  * @mode: display mode
4223a85cb24fSFrançois Tigeot  *
4224a85cb24fSFrançois Tigeot  * Determine the default RGB quantization range for the mode,
4225a85cb24fSFrançois Tigeot  * as specified in CEA-861.
4226a85cb24fSFrançois Tigeot  *
4227a85cb24fSFrançois Tigeot  * Return: The default RGB quantization range for the mode
4228a85cb24fSFrançois Tigeot  */
4229a85cb24fSFrançois Tigeot enum hdmi_quantization_range
drm_default_rgb_quant_range(const struct drm_display_mode * mode)4230a85cb24fSFrançois Tigeot drm_default_rgb_quant_range(const struct drm_display_mode *mode)
4231a85cb24fSFrançois Tigeot {
4232a85cb24fSFrançois Tigeot 	/* All CEA modes other than VIC 1 use limited quantization range. */
4233a85cb24fSFrançois Tigeot 	return drm_match_cea_mode(mode) > 1 ?
4234a85cb24fSFrançois Tigeot 		HDMI_QUANTIZATION_RANGE_LIMITED :
4235a85cb24fSFrançois Tigeot 		HDMI_QUANTIZATION_RANGE_FULL;
4236a85cb24fSFrançois Tigeot }
4237a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_default_rgb_quant_range);
4238a85cb24fSFrançois Tigeot 
drm_parse_ycbcr420_deep_color_info(struct drm_connector * connector,const u8 * db)4239*3f2dd94aSFrançois Tigeot static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
4240*3f2dd94aSFrançois Tigeot 					       const u8 *db)
4241*3f2dd94aSFrançois Tigeot {
4242*3f2dd94aSFrançois Tigeot 	u8 dc_mask;
4243*3f2dd94aSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
4244*3f2dd94aSFrançois Tigeot 
4245*3f2dd94aSFrançois Tigeot 	dc_mask = db[7] & DRM_EDID_YCBCR420_DC_MASK;
4246*3f2dd94aSFrançois Tigeot 	hdmi->y420_dc_modes |= dc_mask;
4247*3f2dd94aSFrançois Tigeot }
4248*3f2dd94aSFrançois Tigeot 
drm_parse_hdmi_forum_vsdb(struct drm_connector * connector,const u8 * hf_vsdb)4249a85cb24fSFrançois Tigeot static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
4250a85cb24fSFrançois Tigeot 				 const u8 *hf_vsdb)
4251a85cb24fSFrançois Tigeot {
4252a85cb24fSFrançois Tigeot 	struct drm_display_info *display = &connector->display_info;
4253a85cb24fSFrançois Tigeot 	struct drm_hdmi_info *hdmi = &display->hdmi;
4254a85cb24fSFrançois Tigeot 
4255a85cb24fSFrançois Tigeot 	if (hf_vsdb[6] & 0x80) {
4256a85cb24fSFrançois Tigeot 		hdmi->scdc.supported = true;
4257a85cb24fSFrançois Tigeot 		if (hf_vsdb[6] & 0x40)
4258a85cb24fSFrançois Tigeot 			hdmi->scdc.read_request = true;
4259a85cb24fSFrançois Tigeot 	}
4260a85cb24fSFrançois Tigeot 
4261a85cb24fSFrançois Tigeot 	/*
4262a85cb24fSFrançois Tigeot 	 * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
4263a85cb24fSFrançois Tigeot 	 * And as per the spec, three factors confirm this:
4264a85cb24fSFrançois Tigeot 	 * * Availability of a HF-VSDB block in EDID (check)
4265a85cb24fSFrançois Tigeot 	 * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
4266a85cb24fSFrançois Tigeot 	 * * SCDC support available (let's check)
4267a85cb24fSFrançois Tigeot 	 * Lets check it out.
4268a85cb24fSFrançois Tigeot 	 */
4269a85cb24fSFrançois Tigeot 
4270a85cb24fSFrançois Tigeot 	if (hf_vsdb[5]) {
4271a85cb24fSFrançois Tigeot 		/* max clock is 5000 KHz times block value */
4272a85cb24fSFrançois Tigeot 		u32 max_tmds_clock = hf_vsdb[5] * 5000;
4273a85cb24fSFrançois Tigeot 		struct drm_scdc *scdc = &hdmi->scdc;
4274a85cb24fSFrançois Tigeot 
4275a85cb24fSFrançois Tigeot 		if (max_tmds_clock > 340000) {
4276a85cb24fSFrançois Tigeot 			display->max_tmds_clock = max_tmds_clock;
4277a85cb24fSFrançois Tigeot 			DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n",
4278a85cb24fSFrançois Tigeot 				display->max_tmds_clock);
4279a85cb24fSFrançois Tigeot 		}
4280a85cb24fSFrançois Tigeot 
4281a85cb24fSFrançois Tigeot 		if (scdc->supported) {
4282a85cb24fSFrançois Tigeot 			scdc->scrambling.supported = true;
4283a85cb24fSFrançois Tigeot 
4284a85cb24fSFrançois Tigeot 			/* Few sinks support scrambling for cloks < 340M */
4285a85cb24fSFrançois Tigeot 			if ((hf_vsdb[6] & 0x8))
4286a85cb24fSFrançois Tigeot 				scdc->scrambling.low_rates = true;
4287a85cb24fSFrançois Tigeot 		}
4288a85cb24fSFrançois Tigeot 	}
4289*3f2dd94aSFrançois Tigeot 
4290*3f2dd94aSFrançois Tigeot 	drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
4291a85cb24fSFrançois Tigeot }
4292a85cb24fSFrançois Tigeot 
drm_parse_hdmi_deep_color_info(struct drm_connector * connector,const u8 * hdmi)42931dedbd3bSFrançois Tigeot static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
42941dedbd3bSFrançois Tigeot 					   const u8 *hdmi)
4295ba55f2f5SFrançois Tigeot {
42961dedbd3bSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
4297ba55f2f5SFrançois Tigeot 	unsigned int dc_bpc = 0;
4298ba55f2f5SFrançois Tigeot 
4299ba55f2f5SFrançois Tigeot 	/* HDMI supports at least 8 bpc */
4300ba55f2f5SFrançois Tigeot 	info->bpc = 8;
4301ba55f2f5SFrançois Tigeot 
4302ba55f2f5SFrançois Tigeot 	if (cea_db_payload_len(hdmi) < 6)
43031dedbd3bSFrançois Tigeot 		return;
4304ba55f2f5SFrançois Tigeot 
4305ba55f2f5SFrançois Tigeot 	if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
4306ba55f2f5SFrançois Tigeot 		dc_bpc = 10;
4307c6f73aabSFrançois Tigeot 		info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
4308ba55f2f5SFrançois Tigeot 		DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
4309ba55f2f5SFrançois Tigeot 			  connector->name);
4310ba55f2f5SFrançois Tigeot 	}
4311ba55f2f5SFrançois Tigeot 
4312ba55f2f5SFrançois Tigeot 	if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
4313ba55f2f5SFrançois Tigeot 		dc_bpc = 12;
4314c6f73aabSFrançois Tigeot 		info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
4315ba55f2f5SFrançois Tigeot 		DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
4316ba55f2f5SFrançois Tigeot 			  connector->name);
4317ba55f2f5SFrançois Tigeot 	}
4318ba55f2f5SFrançois Tigeot 
4319ba55f2f5SFrançois Tigeot 	if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
4320ba55f2f5SFrançois Tigeot 		dc_bpc = 16;
4321c6f73aabSFrançois Tigeot 		info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
4322ba55f2f5SFrançois Tigeot 		DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
4323ba55f2f5SFrançois Tigeot 			  connector->name);
4324ba55f2f5SFrançois Tigeot 	}
4325ba55f2f5SFrançois Tigeot 
43261dedbd3bSFrançois Tigeot 	if (dc_bpc == 0) {
43271dedbd3bSFrançois Tigeot 		DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
43281dedbd3bSFrançois Tigeot 			  connector->name);
43291dedbd3bSFrançois Tigeot 		return;
43301dedbd3bSFrançois Tigeot 	}
43311dedbd3bSFrançois Tigeot 
4332ba55f2f5SFrançois Tigeot 	DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
4333ba55f2f5SFrançois Tigeot 		  connector->name, dc_bpc);
4334ba55f2f5SFrançois Tigeot 	info->bpc = dc_bpc;
4335ba55f2f5SFrançois Tigeot 
4336ba55f2f5SFrançois Tigeot 	/*
4337ba55f2f5SFrançois Tigeot 	 * Deep color support mandates RGB444 support for all video
4338ba55f2f5SFrançois Tigeot 	 * modes and forbids YCRCB422 support for all video modes per
4339ba55f2f5SFrançois Tigeot 	 * HDMI 1.3 spec.
4340ba55f2f5SFrançois Tigeot 	 */
4341ba55f2f5SFrançois Tigeot 	info->color_formats = DRM_COLOR_FORMAT_RGB444;
4342ba55f2f5SFrançois Tigeot 
4343ba55f2f5SFrançois Tigeot 	/* YCRCB444 is optional according to spec. */
4344ba55f2f5SFrançois Tigeot 	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
4345ba55f2f5SFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
4346ba55f2f5SFrançois Tigeot 		DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
4347ba55f2f5SFrançois Tigeot 			  connector->name);
4348ba55f2f5SFrançois Tigeot 	}
4349ba55f2f5SFrançois Tigeot 
4350ba55f2f5SFrançois Tigeot 	/*
4351ba55f2f5SFrançois Tigeot 	 * Spec says that if any deep color mode is supported at all,
4352ba55f2f5SFrançois Tigeot 	 * then deep color 36 bit must be supported.
4353ba55f2f5SFrançois Tigeot 	 */
4354ba55f2f5SFrançois Tigeot 	if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
4355ba55f2f5SFrançois Tigeot 		DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
4356ba55f2f5SFrançois Tigeot 			  connector->name);
4357ba55f2f5SFrançois Tigeot 	}
4358ba55f2f5SFrançois Tigeot }
4359ba55f2f5SFrançois Tigeot 
43601dedbd3bSFrançois Tigeot static void
drm_parse_hdmi_vsdb_video(struct drm_connector * connector,const u8 * db)43611dedbd3bSFrançois Tigeot drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
43625718399fSFrançois Tigeot {
43631dedbd3bSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
43641dedbd3bSFrançois Tigeot 	u8 len = cea_db_payload_len(db);
43655718399fSFrançois Tigeot 
43661dedbd3bSFrançois Tigeot 	if (len >= 6)
43671dedbd3bSFrançois Tigeot 		info->dvi_dual = db[6] & 1;
43681dedbd3bSFrançois Tigeot 	if (len >= 7)
43691dedbd3bSFrançois Tigeot 		info->max_tmds_clock = db[7] * 5000;
43705718399fSFrançois Tigeot 
43711dedbd3bSFrançois Tigeot 	DRM_DEBUG_KMS("HDMI: DVI dual %d, "
43721dedbd3bSFrançois Tigeot 		      "max TMDS clock %d kHz\n",
43731dedbd3bSFrançois Tigeot 		      info->dvi_dual,
43741dedbd3bSFrançois Tigeot 		      info->max_tmds_clock);
43755718399fSFrançois Tigeot 
43761dedbd3bSFrançois Tigeot 	drm_parse_hdmi_deep_color_info(connector, db);
43771dedbd3bSFrançois Tigeot }
43785718399fSFrançois Tigeot 
drm_parse_cea_ext(struct drm_connector * connector,const struct edid * edid)43791dedbd3bSFrançois Tigeot static void drm_parse_cea_ext(struct drm_connector *connector,
4380*3f2dd94aSFrançois Tigeot 			      const struct edid *edid)
43811dedbd3bSFrançois Tigeot {
43821dedbd3bSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
43831dedbd3bSFrançois Tigeot 	const u8 *edid_ext;
43841dedbd3bSFrançois Tigeot 	int i, start, end;
43855718399fSFrançois Tigeot 
43866e29dde8SFrançois Tigeot 	edid_ext = drm_find_cea_extension(edid);
43871dedbd3bSFrançois Tigeot 	if (!edid_ext)
43881dedbd3bSFrançois Tigeot 		return;
43891dedbd3bSFrançois Tigeot 
43906e29dde8SFrançois Tigeot 	info->cea_rev = edid_ext[1];
43916e29dde8SFrançois Tigeot 
43926e29dde8SFrançois Tigeot 	/* The existence of a CEA block should imply RGB support */
43936e29dde8SFrançois Tigeot 	info->color_formats = DRM_COLOR_FORMAT_RGB444;
43946e29dde8SFrançois Tigeot 	if (edid_ext[3] & EDID_CEA_YCRCB444)
43956e29dde8SFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
43966e29dde8SFrançois Tigeot 	if (edid_ext[3] & EDID_CEA_YCRCB422)
43976e29dde8SFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
43981dedbd3bSFrançois Tigeot 
43991dedbd3bSFrançois Tigeot 	if (cea_db_offsets(edid_ext, &start, &end))
44001dedbd3bSFrançois Tigeot 		return;
44011dedbd3bSFrançois Tigeot 
44021dedbd3bSFrançois Tigeot 	for_each_cea_db(edid_ext, i, start, end) {
44031dedbd3bSFrançois Tigeot 		const u8 *db = &edid_ext[i];
44041dedbd3bSFrançois Tigeot 
44051dedbd3bSFrançois Tigeot 		if (cea_db_is_hdmi_vsdb(db))
44061dedbd3bSFrançois Tigeot 			drm_parse_hdmi_vsdb_video(connector, db);
4407a85cb24fSFrançois Tigeot 		if (cea_db_is_hdmi_forum_vsdb(db))
4408a85cb24fSFrançois Tigeot 			drm_parse_hdmi_forum_vsdb(connector, db);
4409*3f2dd94aSFrançois Tigeot 		if (cea_db_is_y420cmdb(db))
4410*3f2dd94aSFrançois Tigeot 			drm_parse_y420cmdb_bitmap(connector, db);
44111dedbd3bSFrançois Tigeot 	}
44126e29dde8SFrançois Tigeot }
44136e29dde8SFrançois Tigeot 
4414*3f2dd94aSFrançois Tigeot /* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
4415*3f2dd94aSFrançois Tigeot  * all of the values which would have been set from EDID
4416*3f2dd94aSFrançois Tigeot  */
4417*3f2dd94aSFrançois Tigeot void
drm_reset_display_info(struct drm_connector * connector)4418*3f2dd94aSFrançois Tigeot drm_reset_display_info(struct drm_connector *connector)
44191dedbd3bSFrançois Tigeot {
44201dedbd3bSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
44211dedbd3bSFrançois Tigeot 
4422*3f2dd94aSFrançois Tigeot 	info->width_mm = 0;
4423*3f2dd94aSFrançois Tigeot 	info->height_mm = 0;
4424*3f2dd94aSFrançois Tigeot 
4425*3f2dd94aSFrançois Tigeot 	info->bpc = 0;
4426*3f2dd94aSFrançois Tigeot 	info->color_formats = 0;
4427*3f2dd94aSFrançois Tigeot 	info->cea_rev = 0;
4428*3f2dd94aSFrançois Tigeot 	info->max_tmds_clock = 0;
4429*3f2dd94aSFrançois Tigeot 	info->dvi_dual = false;
4430*3f2dd94aSFrançois Tigeot 
4431*3f2dd94aSFrançois Tigeot 	info->non_desktop = 0;
4432*3f2dd94aSFrançois Tigeot }
4433*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL_GPL(drm_reset_display_info);
4434*3f2dd94aSFrançois Tigeot 
drm_add_display_info(struct drm_connector * connector,const struct edid * edid)4435*3f2dd94aSFrançois Tigeot u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
4436*3f2dd94aSFrançois Tigeot {
4437*3f2dd94aSFrançois Tigeot 	struct drm_display_info *info = &connector->display_info;
4438*3f2dd94aSFrançois Tigeot 
4439*3f2dd94aSFrançois Tigeot 	u32 quirks = edid_get_quirks(edid);
4440*3f2dd94aSFrançois Tigeot 
44411dedbd3bSFrançois Tigeot 	info->width_mm = edid->width_cm * 10;
44421dedbd3bSFrançois Tigeot 	info->height_mm = edid->height_cm * 10;
44431dedbd3bSFrançois Tigeot 
44441dedbd3bSFrançois Tigeot 	/* driver figures it out in this case */
44451dedbd3bSFrançois Tigeot 	info->bpc = 0;
44461dedbd3bSFrançois Tigeot 	info->color_formats = 0;
44471dedbd3bSFrançois Tigeot 	info->cea_rev = 0;
44481dedbd3bSFrançois Tigeot 	info->max_tmds_clock = 0;
44491dedbd3bSFrançois Tigeot 	info->dvi_dual = false;
44501dedbd3bSFrançois Tigeot 
4451*3f2dd94aSFrançois Tigeot 	info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
4452*3f2dd94aSFrançois Tigeot 
4453*3f2dd94aSFrançois Tigeot 	DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);
4454*3f2dd94aSFrançois Tigeot 
44551dedbd3bSFrançois Tigeot 	if (edid->revision < 3)
4456*3f2dd94aSFrançois Tigeot 		return quirks;
44571dedbd3bSFrançois Tigeot 
44581dedbd3bSFrançois Tigeot 	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
4459*3f2dd94aSFrançois Tigeot 		return quirks;
44601dedbd3bSFrançois Tigeot 
44611dedbd3bSFrançois Tigeot 	drm_parse_cea_ext(connector, edid);
44621dedbd3bSFrançois Tigeot 
44631dedbd3bSFrançois Tigeot 	/*
44641dedbd3bSFrançois Tigeot 	 * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
44651dedbd3bSFrançois Tigeot 	 *
44661dedbd3bSFrançois Tigeot 	 * For such displays, the DFP spec 1.0, section 3.10 "EDID support"
44671dedbd3bSFrançois Tigeot 	 * tells us to assume 8 bpc color depth if the EDID doesn't have
44681dedbd3bSFrançois Tigeot 	 * extensions which tell otherwise.
44691dedbd3bSFrançois Tigeot 	 */
44701dedbd3bSFrançois Tigeot 	if ((info->bpc == 0) && (edid->revision < 4) &&
44711dedbd3bSFrançois Tigeot 	    (edid->input & DRM_EDID_DIGITAL_TYPE_DVI)) {
44721dedbd3bSFrançois Tigeot 		info->bpc = 8;
44731dedbd3bSFrançois Tigeot 		DRM_DEBUG("%s: Assigning DFP sink color depth as %d bpc.\n",
44741dedbd3bSFrançois Tigeot 			  connector->name, info->bpc);
44751dedbd3bSFrançois Tigeot 	}
4476ba55f2f5SFrançois Tigeot 
44776e29dde8SFrançois Tigeot 	/* Only defined for 1.4 with digital displays */
44786e29dde8SFrançois Tigeot 	if (edid->revision < 4)
4479*3f2dd94aSFrançois Tigeot 		return quirks;
44806e29dde8SFrançois Tigeot 
44815718399fSFrançois Tigeot 	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
44825718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_6:
44835718399fSFrançois Tigeot 		info->bpc = 6;
44845718399fSFrançois Tigeot 		break;
44855718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_8:
44865718399fSFrançois Tigeot 		info->bpc = 8;
44875718399fSFrançois Tigeot 		break;
44885718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_10:
44895718399fSFrançois Tigeot 		info->bpc = 10;
44905718399fSFrançois Tigeot 		break;
44915718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_12:
44925718399fSFrançois Tigeot 		info->bpc = 12;
44935718399fSFrançois Tigeot 		break;
44945718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_14:
44955718399fSFrançois Tigeot 		info->bpc = 14;
44965718399fSFrançois Tigeot 		break;
44975718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_16:
44985718399fSFrançois Tigeot 		info->bpc = 16;
44995718399fSFrançois Tigeot 		break;
45005718399fSFrançois Tigeot 	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
45015718399fSFrançois Tigeot 	default:
45025718399fSFrançois Tigeot 		info->bpc = 0;
45035718399fSFrançois Tigeot 		break;
45045718399fSFrançois Tigeot 	}
45055718399fSFrançois Tigeot 
4506ba55f2f5SFrançois Tigeot 	DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
4507ba55f2f5SFrançois Tigeot 			  connector->name, info->bpc);
4508ba55f2f5SFrançois Tigeot 
45096e29dde8SFrançois Tigeot 	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
45106e29dde8SFrançois Tigeot 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
45116e29dde8SFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
45126e29dde8SFrançois Tigeot 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
45136e29dde8SFrançois Tigeot 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
4514*3f2dd94aSFrançois Tigeot 	return quirks;
45155718399fSFrançois Tigeot }
4516*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL_GPL(drm_add_display_info);
45175718399fSFrançois Tigeot 
validate_displayid(u8 * displayid,int length,int idx)45188621f407SFrançois Tigeot static int validate_displayid(u8 *displayid, int length, int idx)
45198621f407SFrançois Tigeot {
45208621f407SFrançois Tigeot 	int i;
45218621f407SFrançois Tigeot 	u8 csum = 0;
45228621f407SFrançois Tigeot 	struct displayid_hdr *base;
45238621f407SFrançois Tigeot 
45248621f407SFrançois Tigeot 	base = (struct displayid_hdr *)&displayid[idx];
45258621f407SFrançois Tigeot 
45268621f407SFrançois Tigeot 	DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
45278621f407SFrançois Tigeot 		      base->rev, base->bytes, base->prod_id, base->ext_count);
45288621f407SFrançois Tigeot 
45298621f407SFrançois Tigeot 	if (base->bytes + 5 > length - idx)
45308621f407SFrançois Tigeot 		return -EINVAL;
45318621f407SFrançois Tigeot 	for (i = idx; i <= base->bytes + 5; i++) {
45328621f407SFrançois Tigeot 		csum += displayid[i];
45338621f407SFrançois Tigeot 	}
45348621f407SFrançois Tigeot 	if (csum) {
4535a85cb24fSFrançois Tigeot 		DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
45368621f407SFrançois Tigeot 		return -EINVAL;
45378621f407SFrançois Tigeot 	}
45388621f407SFrançois Tigeot 	return 0;
45398621f407SFrançois Tigeot }
45408621f407SFrançois Tigeot 
drm_mode_displayid_detailed(struct drm_device * dev,struct displayid_detailed_timings_1 * timings)45418621f407SFrançois Tigeot static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
45428621f407SFrançois Tigeot 							    struct displayid_detailed_timings_1 *timings)
45438621f407SFrançois Tigeot {
45448621f407SFrançois Tigeot 	struct drm_display_mode *mode;
45458621f407SFrançois Tigeot 	unsigned pixel_clock = (timings->pixel_clock[0] |
45468621f407SFrançois Tigeot 				(timings->pixel_clock[1] << 8) |
45478621f407SFrançois Tigeot 				(timings->pixel_clock[2] << 16));
45488621f407SFrançois Tigeot 	unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
45498621f407SFrançois Tigeot 	unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
45508621f407SFrançois Tigeot 	unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
45518621f407SFrançois Tigeot 	unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
45528621f407SFrançois Tigeot 	unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
45538621f407SFrançois Tigeot 	unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1;
45548621f407SFrançois Tigeot 	unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1;
45558621f407SFrançois Tigeot 	unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
45568621f407SFrançois Tigeot 	bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
45578621f407SFrançois Tigeot 	bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
45588621f407SFrançois Tigeot 	mode = drm_mode_create(dev);
45598621f407SFrançois Tigeot 	if (!mode)
45608621f407SFrançois Tigeot 		return NULL;
45618621f407SFrançois Tigeot 
45628621f407SFrançois Tigeot 	mode->clock = pixel_clock * 10;
45638621f407SFrançois Tigeot 	mode->hdisplay = hactive;
45648621f407SFrançois Tigeot 	mode->hsync_start = mode->hdisplay + hsync;
45658621f407SFrançois Tigeot 	mode->hsync_end = mode->hsync_start + hsync_width;
45668621f407SFrançois Tigeot 	mode->htotal = mode->hdisplay + hblank;
45678621f407SFrançois Tigeot 
45688621f407SFrançois Tigeot 	mode->vdisplay = vactive;
45698621f407SFrançois Tigeot 	mode->vsync_start = mode->vdisplay + vsync;
45708621f407SFrançois Tigeot 	mode->vsync_end = mode->vsync_start + vsync_width;
45718621f407SFrançois Tigeot 	mode->vtotal = mode->vdisplay + vblank;
45728621f407SFrançois Tigeot 
45738621f407SFrançois Tigeot 	mode->flags = 0;
45748621f407SFrançois Tigeot 	mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
45758621f407SFrançois Tigeot 	mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
45768621f407SFrançois Tigeot 	mode->type = DRM_MODE_TYPE_DRIVER;
45778621f407SFrançois Tigeot 
45788621f407SFrançois Tigeot 	if (timings->flags & 0x80)
45798621f407SFrançois Tigeot 		mode->type |= DRM_MODE_TYPE_PREFERRED;
45808621f407SFrançois Tigeot 	mode->vrefresh = drm_mode_vrefresh(mode);
45818621f407SFrançois Tigeot 	drm_mode_set_name(mode);
45828621f407SFrançois Tigeot 
45838621f407SFrançois Tigeot 	return mode;
45848621f407SFrançois Tigeot }
45858621f407SFrançois Tigeot 
add_displayid_detailed_1_modes(struct drm_connector * connector,struct displayid_block * block)45868621f407SFrançois Tigeot static int add_displayid_detailed_1_modes(struct drm_connector *connector,
45878621f407SFrançois Tigeot 					  struct displayid_block *block)
45888621f407SFrançois Tigeot {
45898621f407SFrançois Tigeot 	struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block;
45908621f407SFrançois Tigeot 	int i;
45918621f407SFrançois Tigeot 	int num_timings;
45928621f407SFrançois Tigeot 	struct drm_display_mode *newmode;
45938621f407SFrançois Tigeot 	int num_modes = 0;
45948621f407SFrançois Tigeot 	/* blocks must be multiple of 20 bytes length */
45958621f407SFrançois Tigeot 	if (block->num_bytes % 20)
45968621f407SFrançois Tigeot 		return 0;
45978621f407SFrançois Tigeot 
45988621f407SFrançois Tigeot 	num_timings = block->num_bytes / 20;
45998621f407SFrançois Tigeot 	for (i = 0; i < num_timings; i++) {
46008621f407SFrançois Tigeot 		struct displayid_detailed_timings_1 *timings = &det->timings[i];
46018621f407SFrançois Tigeot 
46028621f407SFrançois Tigeot 		newmode = drm_mode_displayid_detailed(connector->dev, timings);
46038621f407SFrançois Tigeot 		if (!newmode)
46048621f407SFrançois Tigeot 			continue;
46058621f407SFrançois Tigeot 
46068621f407SFrançois Tigeot 		drm_mode_probed_add(connector, newmode);
46078621f407SFrançois Tigeot 		num_modes++;
46088621f407SFrançois Tigeot 	}
46098621f407SFrançois Tigeot 	return num_modes;
46108621f407SFrançois Tigeot }
46118621f407SFrançois Tigeot 
add_displayid_detailed_modes(struct drm_connector * connector,struct edid * edid)46128621f407SFrançois Tigeot static int add_displayid_detailed_modes(struct drm_connector *connector,
46138621f407SFrançois Tigeot 					struct edid *edid)
46148621f407SFrançois Tigeot {
46158621f407SFrançois Tigeot 	u8 *displayid;
46168621f407SFrançois Tigeot 	int ret;
46178621f407SFrançois Tigeot 	int idx = 1;
46188621f407SFrançois Tigeot 	int length = EDID_LENGTH;
46198621f407SFrançois Tigeot 	struct displayid_block *block;
46208621f407SFrançois Tigeot 	int num_modes = 0;
46218621f407SFrançois Tigeot 
46228621f407SFrançois Tigeot 	displayid = drm_find_displayid_extension(edid);
46238621f407SFrançois Tigeot 	if (!displayid)
46248621f407SFrançois Tigeot 		return 0;
46258621f407SFrançois Tigeot 
46268621f407SFrançois Tigeot 	ret = validate_displayid(displayid, length, idx);
46278621f407SFrançois Tigeot 	if (ret)
46288621f407SFrançois Tigeot 		return 0;
46298621f407SFrançois Tigeot 
46308621f407SFrançois Tigeot 	idx += sizeof(struct displayid_hdr);
46318621f407SFrançois Tigeot 	while (block = (struct displayid_block *)&displayid[idx],
46328621f407SFrançois Tigeot 	       idx + sizeof(struct displayid_block) <= length &&
46338621f407SFrançois Tigeot 	       idx + sizeof(struct displayid_block) + block->num_bytes <= length &&
46348621f407SFrançois Tigeot 	       block->num_bytes > 0) {
46358621f407SFrançois Tigeot 		idx += block->num_bytes + sizeof(struct displayid_block);
46368621f407SFrançois Tigeot 		switch (block->tag) {
46378621f407SFrançois Tigeot 		case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
46388621f407SFrançois Tigeot 			num_modes += add_displayid_detailed_1_modes(connector, block);
46398621f407SFrançois Tigeot 			break;
46408621f407SFrançois Tigeot 		}
46418621f407SFrançois Tigeot 	}
46428621f407SFrançois Tigeot 	return num_modes;
46438621f407SFrançois Tigeot }
46448621f407SFrançois Tigeot 
46455718399fSFrançois Tigeot /**
46465718399fSFrançois Tigeot  * drm_add_edid_modes - add modes from EDID data, if available
46475718399fSFrançois Tigeot  * @connector: connector we're probing
4648ba55f2f5SFrançois Tigeot  * @edid: EDID data
46495718399fSFrançois Tigeot  *
46501dedbd3bSFrançois Tigeot  * Add the specified modes to the connector's mode list. Also fills out the
46511dedbd3bSFrançois Tigeot  * &drm_display_info structure in @connector with any information which can be
46521dedbd3bSFrançois Tigeot  * derived from the edid.
46535718399fSFrançois Tigeot  *
4654ba55f2f5SFrançois Tigeot  * Return: The number of modes added or 0 if we couldn't find any.
46555718399fSFrançois Tigeot  */
drm_add_edid_modes(struct drm_connector * connector,struct edid * edid)46565718399fSFrançois Tigeot int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
46575718399fSFrançois Tigeot {
46585718399fSFrançois Tigeot 	int num_modes = 0;
46595718399fSFrançois Tigeot 	u32 quirks;
46605718399fSFrançois Tigeot 
46615718399fSFrançois Tigeot 	if (edid == NULL) {
46625718399fSFrançois Tigeot 		return 0;
46635718399fSFrançois Tigeot 	}
46645718399fSFrançois Tigeot 	if (!drm_edid_is_valid(edid)) {
46656e29dde8SFrançois Tigeot 		dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
4666ba55f2f5SFrançois Tigeot 			 connector->name);
46675718399fSFrançois Tigeot 		return 0;
46685718399fSFrançois Tigeot 	}
46695718399fSFrançois Tigeot 
4670*3f2dd94aSFrançois Tigeot 	/*
4671*3f2dd94aSFrançois Tigeot 	 * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
4672*3f2dd94aSFrançois Tigeot 	 * To avoid multiple parsing of same block, lets parse that map
4673*3f2dd94aSFrançois Tigeot 	 * from sink info, before parsing CEA modes.
4674*3f2dd94aSFrançois Tigeot 	 */
4675*3f2dd94aSFrançois Tigeot 	quirks = drm_add_display_info(connector, edid);
46765718399fSFrançois Tigeot 
46775718399fSFrançois Tigeot 	/*
46785718399fSFrançois Tigeot 	 * EDID spec says modes should be preferred in this order:
46795718399fSFrançois Tigeot 	 * - preferred detailed mode
46805718399fSFrançois Tigeot 	 * - other detailed modes from base block
46815718399fSFrançois Tigeot 	 * - detailed modes from extension blocks
46825718399fSFrançois Tigeot 	 * - CVT 3-byte code modes
46835718399fSFrançois Tigeot 	 * - standard timing codes
46845718399fSFrançois Tigeot 	 * - established timing codes
46855718399fSFrançois Tigeot 	 * - modes inferred from GTF or CVT range information
46865718399fSFrançois Tigeot 	 *
46875718399fSFrançois Tigeot 	 * We get this pretty much right.
46885718399fSFrançois Tigeot 	 *
46895718399fSFrançois Tigeot 	 * XXX order for additional mode types in extension blocks?
46905718399fSFrançois Tigeot 	 */
46915718399fSFrançois Tigeot 	num_modes += add_detailed_modes(connector, edid, quirks);
46925718399fSFrançois Tigeot 	num_modes += add_cvt_modes(connector, edid);
46935718399fSFrançois Tigeot 	num_modes += add_standard_modes(connector, edid);
46945718399fSFrançois Tigeot 	num_modes += add_established_modes(connector, edid);
46956e29dde8SFrançois Tigeot 	num_modes += add_cea_modes(connector, edid);
46969edbd4a0SFrançois Tigeot 	num_modes += add_alternate_cea_modes(connector, edid);
46978621f407SFrançois Tigeot 	num_modes += add_displayid_detailed_modes(connector, edid);
469819c468b4SFrançois Tigeot 	if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
469919c468b4SFrançois Tigeot 		num_modes += add_inferred_modes(connector, edid);
47005718399fSFrançois Tigeot 
47015718399fSFrançois Tigeot 	if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
47025718399fSFrançois Tigeot 		edid_fixup_preferred(connector, quirks);
47035718399fSFrançois Tigeot 
47048621f407SFrançois Tigeot 	if (quirks & EDID_QUIRK_FORCE_6BPC)
47058621f407SFrançois Tigeot 		connector->display_info.bpc = 6;
47068621f407SFrançois Tigeot 
47079edbd4a0SFrançois Tigeot 	if (quirks & EDID_QUIRK_FORCE_8BPC)
47089edbd4a0SFrançois Tigeot 		connector->display_info.bpc = 8;
47099edbd4a0SFrançois Tigeot 
4710a85cb24fSFrançois Tigeot 	if (quirks & EDID_QUIRK_FORCE_10BPC)
4711a85cb24fSFrançois Tigeot 		connector->display_info.bpc = 10;
4712a85cb24fSFrançois Tigeot 
4713ba55f2f5SFrançois Tigeot 	if (quirks & EDID_QUIRK_FORCE_12BPC)
4714ba55f2f5SFrançois Tigeot 		connector->display_info.bpc = 12;
4715ba55f2f5SFrançois Tigeot 
47165718399fSFrançois Tigeot 	return num_modes;
47175718399fSFrançois Tigeot }
47186e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_edid_modes);
47195718399fSFrançois Tigeot 
47205718399fSFrançois Tigeot /**
47215718399fSFrançois Tigeot  * drm_add_modes_noedid - add modes for the connectors without EDID
47225718399fSFrançois Tigeot  * @connector: connector we're probing
47235718399fSFrançois Tigeot  * @hdisplay: the horizontal display limit
47245718399fSFrançois Tigeot  * @vdisplay: the vertical display limit
47255718399fSFrançois Tigeot  *
47265718399fSFrançois Tigeot  * Add the specified modes to the connector's mode list. Only when the
47275718399fSFrançois Tigeot  * hdisplay/vdisplay is not beyond the given limit, it will be added.
47285718399fSFrançois Tigeot  *
4729ba55f2f5SFrançois Tigeot  * Return: The number of modes added or 0 if we couldn't find any.
47305718399fSFrançois Tigeot  */
drm_add_modes_noedid(struct drm_connector * connector,int hdisplay,int vdisplay)47315718399fSFrançois Tigeot int drm_add_modes_noedid(struct drm_connector *connector,
47325718399fSFrançois Tigeot 			int hdisplay, int vdisplay)
47335718399fSFrançois Tigeot {
47345718399fSFrançois Tigeot 	int i, count, num_modes = 0;
47355718399fSFrançois Tigeot 	struct drm_display_mode *mode;
47365718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
47375718399fSFrançois Tigeot 
4738a05eeebfSFrançois Tigeot 	count = ARRAY_SIZE(drm_dmt_modes);
47395718399fSFrançois Tigeot 	if (hdisplay < 0)
47405718399fSFrançois Tigeot 		hdisplay = 0;
47415718399fSFrançois Tigeot 	if (vdisplay < 0)
47425718399fSFrançois Tigeot 		vdisplay = 0;
47435718399fSFrançois Tigeot 
47445718399fSFrançois Tigeot 	for (i = 0; i < count; i++) {
4745ce3d36d7SFrançois Tigeot 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
47465718399fSFrançois Tigeot 		if (hdisplay && vdisplay) {
47475718399fSFrançois Tigeot 			/*
47485718399fSFrançois Tigeot 			 * Only when two are valid, they will be used to check
47495718399fSFrançois Tigeot 			 * whether the mode should be added to the mode list of
47505718399fSFrançois Tigeot 			 * the connector.
47515718399fSFrançois Tigeot 			 */
47525718399fSFrançois Tigeot 			if (ptr->hdisplay > hdisplay ||
47535718399fSFrançois Tigeot 					ptr->vdisplay > vdisplay)
47545718399fSFrançois Tigeot 				continue;
47555718399fSFrançois Tigeot 		}
47565718399fSFrançois Tigeot 		if (drm_mode_vrefresh(ptr) > 61)
47575718399fSFrançois Tigeot 			continue;
47585718399fSFrançois Tigeot 		mode = drm_mode_duplicate(dev, ptr);
47595718399fSFrançois Tigeot 		if (mode) {
47605718399fSFrançois Tigeot 			drm_mode_probed_add(connector, mode);
47615718399fSFrançois Tigeot 			num_modes++;
47625718399fSFrançois Tigeot 		}
47635718399fSFrançois Tigeot 	}
47645718399fSFrançois Tigeot 	return num_modes;
47655718399fSFrançois Tigeot }
47666e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_add_modes_noedid);
476787cc1051SMichael Neumann 
4768ba55f2f5SFrançois Tigeot /**
4769ba55f2f5SFrançois Tigeot  * drm_set_preferred_mode - Sets the preferred mode of a connector
4770ba55f2f5SFrançois Tigeot  * @connector: connector whose mode list should be processed
4771ba55f2f5SFrançois Tigeot  * @hpref: horizontal resolution of preferred mode
4772ba55f2f5SFrançois Tigeot  * @vpref: vertical resolution of preferred mode
4773ba55f2f5SFrançois Tigeot  *
4774ba55f2f5SFrançois Tigeot  * Marks a mode as preferred if it matches the resolution specified by @hpref
4775ba55f2f5SFrançois Tigeot  * and @vpref.
4776ba55f2f5SFrançois Tigeot  */
drm_set_preferred_mode(struct drm_connector * connector,int hpref,int vpref)47779edbd4a0SFrançois Tigeot void drm_set_preferred_mode(struct drm_connector *connector,
47789edbd4a0SFrançois Tigeot 			   int hpref, int vpref)
47799edbd4a0SFrançois Tigeot {
47809edbd4a0SFrançois Tigeot 	struct drm_display_mode *mode;
47819edbd4a0SFrançois Tigeot 
47829edbd4a0SFrançois Tigeot 	list_for_each_entry(mode, &connector->probed_modes, head) {
4783ba55f2f5SFrançois Tigeot 		if (mode->hdisplay == hpref &&
4784ba55f2f5SFrançois Tigeot 		    mode->vdisplay == vpref)
47859edbd4a0SFrançois Tigeot 			mode->type |= DRM_MODE_TYPE_PREFERRED;
47869edbd4a0SFrançois Tigeot 	}
47879edbd4a0SFrançois Tigeot }
47889edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_set_preferred_mode);
47899edbd4a0SFrançois Tigeot 
479087cc1051SMichael Neumann /**
479187cc1051SMichael Neumann  * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
479287cc1051SMichael Neumann  *                                              data from a DRM display mode
479387cc1051SMichael Neumann  * @frame: HDMI AVI infoframe
479487cc1051SMichael Neumann  * @mode: DRM display mode
4795*3f2dd94aSFrançois Tigeot  * @is_hdmi2_sink: Sink is HDMI 2.0 compliant
479687cc1051SMichael Neumann  *
4797ba55f2f5SFrançois Tigeot  * Return: 0 on success or a negative error code on failure.
479887cc1051SMichael Neumann  */
479987cc1051SMichael Neumann int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe * frame,const struct drm_display_mode * mode,bool is_hdmi2_sink)480087cc1051SMichael Neumann drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
4801*3f2dd94aSFrançois Tigeot 					 const struct drm_display_mode *mode,
4802*3f2dd94aSFrançois Tigeot 					 bool is_hdmi2_sink)
480387cc1051SMichael Neumann {
480487cc1051SMichael Neumann 	int err;
480587cc1051SMichael Neumann 
480687cc1051SMichael Neumann 	if (!frame || !mode)
480787cc1051SMichael Neumann 		return -EINVAL;
480887cc1051SMichael Neumann 
480987cc1051SMichael Neumann 	err = hdmi_avi_infoframe_init(frame);
481087cc1051SMichael Neumann 	if (err < 0)
481187cc1051SMichael Neumann 		return err;
481287cc1051SMichael Neumann 
48139edbd4a0SFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
48149edbd4a0SFrançois Tigeot 		frame->pixel_repeat = 1;
48159edbd4a0SFrançois Tigeot 
481687cc1051SMichael Neumann 	frame->video_code = drm_match_cea_mode(mode);
481787cc1051SMichael Neumann 
4818*3f2dd94aSFrançois Tigeot 	/*
4819*3f2dd94aSFrançois Tigeot 	 * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but
4820*3f2dd94aSFrançois Tigeot 	 * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we
4821*3f2dd94aSFrançois Tigeot 	 * have to make sure we dont break HDMI 1.4 sinks.
4822*3f2dd94aSFrançois Tigeot 	 */
4823*3f2dd94aSFrançois Tigeot 	if (!is_hdmi2_sink && frame->video_code > 64)
4824*3f2dd94aSFrançois Tigeot 		frame->video_code = 0;
4825*3f2dd94aSFrançois Tigeot 
4826*3f2dd94aSFrançois Tigeot 	/*
4827*3f2dd94aSFrançois Tigeot 	 * HDMI spec says if a mode is found in HDMI 1.4b 4K modes
4828*3f2dd94aSFrançois Tigeot 	 * we should send its VIC in vendor infoframes, else send the
4829*3f2dd94aSFrançois Tigeot 	 * VIC in AVI infoframes. Lets check if this mode is present in
4830*3f2dd94aSFrançois Tigeot 	 * HDMI 1.4b 4K modes
4831*3f2dd94aSFrançois Tigeot 	 */
4832*3f2dd94aSFrançois Tigeot 	if (frame->video_code) {
4833*3f2dd94aSFrançois Tigeot 		u8 vendor_if_vic = drm_match_hdmi_mode(mode);
4834*3f2dd94aSFrançois Tigeot 		bool is_s3d = mode->flags & DRM_MODE_FLAG_3D_MASK;
4835*3f2dd94aSFrançois Tigeot 
4836*3f2dd94aSFrançois Tigeot 		if (drm_valid_hdmi_vic(vendor_if_vic) && !is_s3d)
4837*3f2dd94aSFrançois Tigeot 			frame->video_code = 0;
4838*3f2dd94aSFrançois Tigeot 	}
4839*3f2dd94aSFrançois Tigeot 
484087cc1051SMichael Neumann 	frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
4841ba55f2f5SFrançois Tigeot 
484224edb884SFrançois Tigeot 	/*
484324edb884SFrançois Tigeot 	 * Populate picture aspect ratio from either
484424edb884SFrançois Tigeot 	 * user input (if specified) or from the CEA mode list.
484524edb884SFrançois Tigeot 	 */
484624edb884SFrançois Tigeot 	if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
484724edb884SFrançois Tigeot 		mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
484824edb884SFrançois Tigeot 		frame->picture_aspect = mode->picture_aspect_ratio;
484924edb884SFrançois Tigeot 	else if (frame->video_code > 0)
4850ba55f2f5SFrançois Tigeot 		frame->picture_aspect = drm_get_cea_aspect_ratio(
4851ba55f2f5SFrançois Tigeot 						frame->video_code);
4852ba55f2f5SFrançois Tigeot 
485387cc1051SMichael Neumann 	frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
4854ba55f2f5SFrançois Tigeot 	frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
485587cc1051SMichael Neumann 
485687cc1051SMichael Neumann 	return 0;
485787cc1051SMichael Neumann }
485887cc1051SMichael Neumann EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
48599edbd4a0SFrançois Tigeot 
4860a85cb24fSFrançois Tigeot /**
4861a85cb24fSFrançois Tigeot  * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
4862a85cb24fSFrançois Tigeot  *                                        quantization range information
4863a85cb24fSFrançois Tigeot  * @frame: HDMI AVI infoframe
4864a85cb24fSFrançois Tigeot  * @mode: DRM display mode
4865a85cb24fSFrançois Tigeot  * @rgb_quant_range: RGB quantization range (Q)
4866a85cb24fSFrançois Tigeot  * @rgb_quant_range_selectable: Sink support selectable RGB quantization range (QS)
4867a85cb24fSFrançois Tigeot  */
4868a85cb24fSFrançois Tigeot void
drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe * frame,const struct drm_display_mode * mode,enum hdmi_quantization_range rgb_quant_range,bool rgb_quant_range_selectable,bool is_hdmi2_sink)4869a85cb24fSFrançois Tigeot drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
4870a85cb24fSFrançois Tigeot 				   const struct drm_display_mode *mode,
4871a85cb24fSFrançois Tigeot 				   enum hdmi_quantization_range rgb_quant_range,
4872*3f2dd94aSFrançois Tigeot 				   bool rgb_quant_range_selectable,
4873*3f2dd94aSFrançois Tigeot 				   bool is_hdmi2_sink)
4874a85cb24fSFrançois Tigeot {
4875a85cb24fSFrançois Tigeot 	/*
4876a85cb24fSFrançois Tigeot 	 * CEA-861:
4877a85cb24fSFrançois Tigeot 	 * "A Source shall not send a non-zero Q value that does not correspond
4878a85cb24fSFrançois Tigeot 	 *  to the default RGB Quantization Range for the transmitted Picture
4879a85cb24fSFrançois Tigeot 	 *  unless the Sink indicates support for the Q bit in a Video
4880a85cb24fSFrançois Tigeot 	 *  Capabilities Data Block."
4881a85cb24fSFrançois Tigeot 	 *
4882a85cb24fSFrançois Tigeot 	 * HDMI 2.0 recommends sending non-zero Q when it does match the
4883a85cb24fSFrançois Tigeot 	 * default RGB quantization range for the mode, even when QS=0.
4884a85cb24fSFrançois Tigeot 	 */
4885a85cb24fSFrançois Tigeot 	if (rgb_quant_range_selectable ||
4886a85cb24fSFrançois Tigeot 	    rgb_quant_range == drm_default_rgb_quant_range(mode))
4887a85cb24fSFrançois Tigeot 		frame->quantization_range = rgb_quant_range;
4888a85cb24fSFrançois Tigeot 	else
4889a85cb24fSFrançois Tigeot 		frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
4890a85cb24fSFrançois Tigeot 
4891a85cb24fSFrançois Tigeot 	/*
4892a85cb24fSFrançois Tigeot 	 * CEA-861-F:
4893a85cb24fSFrançois Tigeot 	 * "When transmitting any RGB colorimetry, the Source should set the
4894a85cb24fSFrançois Tigeot 	 *  YQ-field to match the RGB Quantization Range being transmitted
4895a85cb24fSFrançois Tigeot 	 *  (e.g., when Limited Range RGB, set YQ=0 or when Full Range RGB,
4896a85cb24fSFrançois Tigeot 	 *  set YQ=1) and the Sink shall ignore the YQ-field."
4897*3f2dd94aSFrançois Tigeot 	 *
4898*3f2dd94aSFrançois Tigeot 	 * Unfortunate certain sinks (eg. VIZ Model 67/E261VA) get confused
4899*3f2dd94aSFrançois Tigeot 	 * by non-zero YQ when receiving RGB. There doesn't seem to be any
4900*3f2dd94aSFrançois Tigeot 	 * good way to tell which version of CEA-861 the sink supports, so
4901*3f2dd94aSFrançois Tigeot 	 * we limit non-zero YQ to HDMI 2.0 sinks only as HDMI 2.0 is based
4902*3f2dd94aSFrançois Tigeot 	 * on on CEA-861-F.
4903a85cb24fSFrançois Tigeot 	 */
4904*3f2dd94aSFrançois Tigeot 	if (!is_hdmi2_sink ||
4905*3f2dd94aSFrançois Tigeot 	    rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED)
4906a85cb24fSFrançois Tigeot 		frame->ycc_quantization_range =
4907a85cb24fSFrançois Tigeot 			HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
4908a85cb24fSFrançois Tigeot 	else
4909a85cb24fSFrançois Tigeot 		frame->ycc_quantization_range =
4910a85cb24fSFrançois Tigeot 			HDMI_YCC_QUANTIZATION_RANGE_FULL;
4911a85cb24fSFrançois Tigeot }
4912a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_hdmi_avi_infoframe_quant_range);
4913a85cb24fSFrançois Tigeot 
49149edbd4a0SFrançois Tigeot static enum hdmi_3d_structure
s3d_structure_from_display_mode(const struct drm_display_mode * mode)49159edbd4a0SFrançois Tigeot s3d_structure_from_display_mode(const struct drm_display_mode *mode)
49169edbd4a0SFrançois Tigeot {
49179edbd4a0SFrançois Tigeot 	u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK;
49189edbd4a0SFrançois Tigeot 
49199edbd4a0SFrançois Tigeot 	switch (layout) {
49209edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_FRAME_PACKING:
49219edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_FRAME_PACKING;
49229edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
49239edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE;
49249edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
49259edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE;
49269edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
49279edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL;
49289edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_L_DEPTH:
49299edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_L_DEPTH;
49309edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
49319edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH;
49329edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
49339edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM;
49349edbd4a0SFrançois Tigeot 	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
49359edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF;
49369edbd4a0SFrançois Tigeot 	default:
49379edbd4a0SFrançois Tigeot 		return HDMI_3D_STRUCTURE_INVALID;
49389edbd4a0SFrançois Tigeot 	}
49399edbd4a0SFrançois Tigeot }
49409edbd4a0SFrançois Tigeot 
49419edbd4a0SFrançois Tigeot /**
49429edbd4a0SFrançois Tigeot  * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
49439edbd4a0SFrançois Tigeot  * data from a DRM display mode
49449edbd4a0SFrançois Tigeot  * @frame: HDMI vendor infoframe
49459edbd4a0SFrançois Tigeot  * @mode: DRM display mode
49469edbd4a0SFrançois Tigeot  *
49479edbd4a0SFrançois Tigeot  * Note that there's is a need to send HDMI vendor infoframes only when using a
49489edbd4a0SFrançois Tigeot  * 4k or stereoscopic 3D mode. So when giving any other mode as input this
49499edbd4a0SFrançois Tigeot  * function will return -EINVAL, error that can be safely ignored.
49509edbd4a0SFrançois Tigeot  *
4951ba55f2f5SFrançois Tigeot  * Return: 0 on success or a negative error code on failure.
49529edbd4a0SFrançois Tigeot  */
49539edbd4a0SFrançois Tigeot int
drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe * frame,const struct drm_display_mode * mode)49549edbd4a0SFrançois Tigeot drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
49559edbd4a0SFrançois Tigeot 					    const struct drm_display_mode *mode)
49569edbd4a0SFrançois Tigeot {
49579edbd4a0SFrançois Tigeot 	int err;
49589edbd4a0SFrançois Tigeot 	u32 s3d_flags;
49599edbd4a0SFrançois Tigeot 	u8 vic;
49609edbd4a0SFrançois Tigeot 
49619edbd4a0SFrançois Tigeot 	if (!frame || !mode)
49629edbd4a0SFrançois Tigeot 		return -EINVAL;
49639edbd4a0SFrançois Tigeot 
49649edbd4a0SFrançois Tigeot 	vic = drm_match_hdmi_mode(mode);
49659edbd4a0SFrançois Tigeot 	s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK;
49669edbd4a0SFrançois Tigeot 
49679edbd4a0SFrançois Tigeot 	if (!vic && !s3d_flags)
49689edbd4a0SFrançois Tigeot 		return -EINVAL;
49699edbd4a0SFrançois Tigeot 
49709edbd4a0SFrançois Tigeot 	if (vic && s3d_flags)
49719edbd4a0SFrançois Tigeot 		return -EINVAL;
49729edbd4a0SFrançois Tigeot 
49739edbd4a0SFrançois Tigeot 	err = hdmi_vendor_infoframe_init(frame);
49749edbd4a0SFrançois Tigeot 	if (err < 0)
49759edbd4a0SFrançois Tigeot 		return err;
49769edbd4a0SFrançois Tigeot 
49779edbd4a0SFrançois Tigeot 	if (vic)
49789edbd4a0SFrançois Tigeot 		frame->vic = vic;
49799edbd4a0SFrançois Tigeot 	else
49809edbd4a0SFrançois Tigeot 		frame->s3d_struct = s3d_structure_from_display_mode(mode);
49819edbd4a0SFrançois Tigeot 
49829edbd4a0SFrançois Tigeot 	return 0;
49839edbd4a0SFrançois Tigeot }
49849edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
49858621f407SFrançois Tigeot 
drm_parse_tiled_block(struct drm_connector * connector,struct displayid_block * block)49868621f407SFrançois Tigeot static int drm_parse_tiled_block(struct drm_connector *connector,
49878621f407SFrançois Tigeot 				 struct displayid_block *block)
49888621f407SFrançois Tigeot {
49898621f407SFrançois Tigeot 	struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;
49908621f407SFrançois Tigeot 	u16 w, h;
49918621f407SFrançois Tigeot 	u8 tile_v_loc, tile_h_loc;
49928621f407SFrançois Tigeot 	u8 num_v_tile, num_h_tile;
49938621f407SFrançois Tigeot 	struct drm_tile_group *tg;
49948621f407SFrançois Tigeot 
49958621f407SFrançois Tigeot 	w = tile->tile_size[0] | tile->tile_size[1] << 8;
49968621f407SFrançois Tigeot 	h = tile->tile_size[2] | tile->tile_size[3] << 8;
49978621f407SFrançois Tigeot 
49988621f407SFrançois Tigeot 	num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
49998621f407SFrançois Tigeot 	num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
50008621f407SFrançois Tigeot 	tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4);
50018621f407SFrançois Tigeot 	tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4);
50028621f407SFrançois Tigeot 
50038621f407SFrançois Tigeot 	connector->has_tile = true;
50048621f407SFrançois Tigeot 	if (tile->tile_cap & 0x80)
50058621f407SFrançois Tigeot 		connector->tile_is_single_monitor = true;
50068621f407SFrançois Tigeot 
50078621f407SFrançois Tigeot 	connector->num_h_tile = num_h_tile + 1;
50088621f407SFrançois Tigeot 	connector->num_v_tile = num_v_tile + 1;
50098621f407SFrançois Tigeot 	connector->tile_h_loc = tile_h_loc;
50108621f407SFrançois Tigeot 	connector->tile_v_loc = tile_v_loc;
50118621f407SFrançois Tigeot 	connector->tile_h_size = w + 1;
50128621f407SFrançois Tigeot 	connector->tile_v_size = h + 1;
50138621f407SFrançois Tigeot 
50148621f407SFrançois Tigeot 	DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap);
50158621f407SFrançois Tigeot 	DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1);
50168621f407SFrançois Tigeot 	DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n",
50178621f407SFrançois Tigeot 		      num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc);
50188621f407SFrançois Tigeot 	DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);
50198621f407SFrançois Tigeot 
50208621f407SFrançois Tigeot 	tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
50218621f407SFrançois Tigeot 	if (!tg) {
50228621f407SFrançois Tigeot 		tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
50238621f407SFrançois Tigeot 	}
50248621f407SFrançois Tigeot 	if (!tg)
50258621f407SFrançois Tigeot 		return -ENOMEM;
50268621f407SFrançois Tigeot 
50278621f407SFrançois Tigeot 	if (connector->tile_group != tg) {
50288621f407SFrançois Tigeot 		/* if we haven't got a pointer,
50298621f407SFrançois Tigeot 		   take the reference, drop ref to old tile group */
50308621f407SFrançois Tigeot 		if (connector->tile_group) {
50318621f407SFrançois Tigeot 			drm_mode_put_tile_group(connector->dev, connector->tile_group);
50328621f407SFrançois Tigeot 		}
50338621f407SFrançois Tigeot 		connector->tile_group = tg;
50348621f407SFrançois Tigeot 	} else
50358621f407SFrançois Tigeot 		/* if same tile group, then release the ref we just took. */
50368621f407SFrançois Tigeot 		drm_mode_put_tile_group(connector->dev, tg);
50378621f407SFrançois Tigeot 	return 0;
50388621f407SFrançois Tigeot }
50398621f407SFrançois Tigeot 
drm_parse_display_id(struct drm_connector * connector,u8 * displayid,int length,bool is_edid_extension)50408621f407SFrançois Tigeot static int drm_parse_display_id(struct drm_connector *connector,
50418621f407SFrançois Tigeot 				u8 *displayid, int length,
50428621f407SFrançois Tigeot 				bool is_edid_extension)
50438621f407SFrançois Tigeot {
50448621f407SFrançois Tigeot 	/* if this is an EDID extension the first byte will be 0x70 */
50458621f407SFrançois Tigeot 	int idx = 0;
50468621f407SFrançois Tigeot 	struct displayid_block *block;
50478621f407SFrançois Tigeot 	int ret;
50488621f407SFrançois Tigeot 
50498621f407SFrançois Tigeot 	if (is_edid_extension)
50508621f407SFrançois Tigeot 		idx = 1;
50518621f407SFrançois Tigeot 
50528621f407SFrançois Tigeot 	ret = validate_displayid(displayid, length, idx);
50538621f407SFrançois Tigeot 	if (ret)
50548621f407SFrançois Tigeot 		return ret;
50558621f407SFrançois Tigeot 
50568621f407SFrançois Tigeot 	idx += sizeof(struct displayid_hdr);
50578621f407SFrançois Tigeot 	while (block = (struct displayid_block *)&displayid[idx],
50588621f407SFrançois Tigeot 	       idx + sizeof(struct displayid_block) <= length &&
50598621f407SFrançois Tigeot 	       idx + sizeof(struct displayid_block) + block->num_bytes <= length &&
50608621f407SFrançois Tigeot 	       block->num_bytes > 0) {
50618621f407SFrançois Tigeot 		idx += block->num_bytes + sizeof(struct displayid_block);
50628621f407SFrançois Tigeot 		DRM_DEBUG_KMS("block id 0x%x, rev %d, len %d\n",
50638621f407SFrançois Tigeot 			      block->tag, block->rev, block->num_bytes);
50648621f407SFrançois Tigeot 
50658621f407SFrançois Tigeot 		switch (block->tag) {
50668621f407SFrançois Tigeot 		case DATA_BLOCK_TILED_DISPLAY:
50678621f407SFrançois Tigeot 			ret = drm_parse_tiled_block(connector, block);
50688621f407SFrançois Tigeot 			if (ret)
50698621f407SFrançois Tigeot 				return ret;
50708621f407SFrançois Tigeot 			break;
50718621f407SFrançois Tigeot 		case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
50728621f407SFrançois Tigeot 			/* handled in mode gathering code. */
50738621f407SFrançois Tigeot 			break;
50748621f407SFrançois Tigeot 		default:
50758621f407SFrançois Tigeot 			DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag);
50768621f407SFrançois Tigeot 			break;
50778621f407SFrançois Tigeot 		}
50788621f407SFrançois Tigeot 	}
50798621f407SFrançois Tigeot 	return 0;
50808621f407SFrançois Tigeot }
50818621f407SFrançois Tigeot 
drm_get_displayid(struct drm_connector * connector,struct edid * edid)50828621f407SFrançois Tigeot static void drm_get_displayid(struct drm_connector *connector,
50838621f407SFrançois Tigeot 			      struct edid *edid)
50848621f407SFrançois Tigeot {
50858621f407SFrançois Tigeot 	void *displayid = NULL;
50868621f407SFrançois Tigeot 	int ret;
50878621f407SFrançois Tigeot 	connector->has_tile = false;
50888621f407SFrançois Tigeot 	displayid = drm_find_displayid_extension(edid);
50898621f407SFrançois Tigeot 	if (!displayid) {
50908621f407SFrançois Tigeot 		/* drop reference to any tile group we had */
50918621f407SFrançois Tigeot 		goto out_drop_ref;
50928621f407SFrançois Tigeot 	}
50938621f407SFrançois Tigeot 
50948621f407SFrançois Tigeot 	ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true);
50958621f407SFrançois Tigeot 	if (ret < 0)
50968621f407SFrançois Tigeot 		goto out_drop_ref;
50978621f407SFrançois Tigeot 	if (!connector->has_tile)
50988621f407SFrançois Tigeot 		goto out_drop_ref;
50998621f407SFrançois Tigeot 	return;
51008621f407SFrançois Tigeot out_drop_ref:
51018621f407SFrançois Tigeot 	if (connector->tile_group) {
51028621f407SFrançois Tigeot 		drm_mode_put_tile_group(connector->dev, connector->tile_group);
51038621f407SFrançois Tigeot 		connector->tile_group = NULL;
51048621f407SFrançois Tigeot 	}
51058621f407SFrançois Tigeot 	return;
51068621f407SFrançois Tigeot }
5107