xref: /openbmc/linux/drivers/gpu/drm/drm_edid.c (revision 360823a09426347ea8f232b0b0b5156d0aed0302)
1f453ba04SDave Airlie /*
2f453ba04SDave Airlie  * Copyright (c) 2006 Luc Verhaegen (quirks list)
3f453ba04SDave Airlie  * Copyright (c) 2007-2008 Intel Corporation
4f453ba04SDave Airlie  *   Jesse Barnes <jesse.barnes@intel.com>
561e57a8dSAdam Jackson  * Copyright 2010 Red Hat, Inc.
6f453ba04SDave Airlie  *
7f453ba04SDave Airlie  * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
8f453ba04SDave Airlie  * FB layer.
9f453ba04SDave Airlie  *   Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
10f453ba04SDave Airlie  *
11f453ba04SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
12f453ba04SDave Airlie  * copy of this software and associated documentation files (the "Software"),
13f453ba04SDave Airlie  * to deal in the Software without restriction, including without limitation
14f453ba04SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sub license,
15f453ba04SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
16f453ba04SDave Airlie  * Software is furnished to do so, subject to the following conditions:
17f453ba04SDave Airlie  *
18f453ba04SDave Airlie  * The above copyright notice and this permission notice (including the
19f453ba04SDave Airlie  * next paragraph) shall be included in all copies or substantial portions
20f453ba04SDave Airlie  * of the Software.
21f453ba04SDave Airlie  *
22f453ba04SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23f453ba04SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24f453ba04SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
25f453ba04SDave Airlie  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26f453ba04SDave Airlie  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27f453ba04SDave Airlie  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28f453ba04SDave Airlie  * DEALINGS IN THE SOFTWARE.
29f453ba04SDave Airlie  */
309c79edecSJani Nikula 
3118a9cbbeSJani Nikula #include <linux/bitfield.h>
3210a85120SThierry Reding #include <linux/hdmi.h>
33f453ba04SDave Airlie #include <linux/i2c.h>
349c79edecSJani Nikula #include <linux/kernel.h>
3547819ba2SAdam Jackson #include <linux/module.h>
3636b73b05SThomas Zimmermann #include <linux/pci.h>
379c79edecSJani Nikula #include <linux/slab.h>
385cb8eaa2SLukas Wunner #include <linux/vga_switcheroo.h>
399c79edecSJani Nikula 
409c79edecSJani Nikula #include <drm/drm_displayid.h>
419c79edecSJani Nikula #include <drm/drm_drv.h>
42760285e7SDavid Howells #include <drm/drm_edid.h>
439338203cSLaurent Pinchart #include <drm/drm_encoder.h>
449c79edecSJani Nikula #include <drm/drm_print.h>
45f453ba04SDave Airlie 
46969218feSTakashi Iwai #include "drm_crtc_internal.h"
47969218feSTakashi Iwai 
oui(u8 first,u8 second,u8 third)4837eab1feSJani Nikula static int oui(u8 first, u8 second, u8 third)
4937eab1feSJani Nikula {
5037eab1feSJani Nikula 	return (first << 16) | (second << 8) | third;
5137eab1feSJani Nikula }
5237eab1feSJani Nikula 
53d1ff6409SAdam Jackson #define EDID_EST_TIMINGS 16
54d1ff6409SAdam Jackson #define EDID_STD_TIMINGS 8
55d1ff6409SAdam Jackson #define EDID_DETAILED_TIMINGS 4
56f453ba04SDave Airlie 
57f453ba04SDave Airlie /*
58f453ba04SDave Airlie  * EDID blocks out in the wild have a variety of bugs, try to collect
59f453ba04SDave Airlie  * them here (note that userspace may work around broken monitors first,
60f453ba04SDave Airlie  * but fixes should make their way here so that the kernel "just works"
61f453ba04SDave Airlie  * on as many displays as possible).
62f453ba04SDave Airlie  */
63f453ba04SDave Airlie 
64f453ba04SDave Airlie /* First detailed mode wrong, use largest 60Hz mode */
65f453ba04SDave Airlie #define EDID_QUIRK_PREFER_LARGE_60		(1 << 0)
66f453ba04SDave Airlie /* Reported 135MHz pixel clock is too high, needs adjustment */
67f453ba04SDave Airlie #define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1)
68f453ba04SDave Airlie /* Prefer the largest mode at 75 Hz */
69f453ba04SDave Airlie #define EDID_QUIRK_PREFER_LARGE_75		(1 << 2)
70f453ba04SDave Airlie /* Detail timing is in cm not mm */
71f453ba04SDave Airlie #define EDID_QUIRK_DETAILED_IN_CM		(1 << 3)
72f453ba04SDave Airlie /* Detailed timing descriptors have bogus size values, so just take the
73f453ba04SDave Airlie  * maximum size and use that.
74f453ba04SDave Airlie  */
75f453ba04SDave Airlie #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4)
76f453ba04SDave Airlie /* use +hsync +vsync for detailed mode */
77f453ba04SDave Airlie #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
78bc42aabcSAdam Jackson /* Force reduced-blanking timings for detailed modes */
79bc42aabcSAdam Jackson #define EDID_QUIRK_FORCE_REDUCED_BLANKING	(1 << 7)
8049d45a31SRafał Miłecki /* Force 8bpc */
8149d45a31SRafał Miłecki #define EDID_QUIRK_FORCE_8BPC			(1 << 8)
82bc5b9641SMario Kleiner /* Force 12bpc */
83bc5b9641SMario Kleiner #define EDID_QUIRK_FORCE_12BPC			(1 << 9)
84e10aec65SMario Kleiner /* Force 6bpc */
85e10aec65SMario Kleiner #define EDID_QUIRK_FORCE_6BPC			(1 << 10)
86e345da82SMario Kleiner /* Force 10bpc */
87e345da82SMario Kleiner #define EDID_QUIRK_FORCE_10BPC			(1 << 11)
8866660d4cSDave Airlie /* Non desktop display (i.e. HMD) */
8966660d4cSDave Airlie #define EDID_QUIRK_NON_DESKTOP			(1 << 12)
90aa193f7eSHamza Mahfooz /* Cap the DSC target bitrate to 15bpp */
91aa193f7eSHamza Mahfooz #define EDID_QUIRK_CAP_DSC_15BPP		(1 << 13)
923c537889SAlex Deucher 
932869f599SPhilipp Zabel #define MICROSOFT_IEEE_OUI	0xca125c
942869f599SPhilipp Zabel 
9513931579SAdam Jackson struct detailed_mode_closure {
9613931579SAdam Jackson 	struct drm_connector *connector;
97dd0f4470SJani Nikula 	const struct drm_edid *drm_edid;
9813931579SAdam Jackson 	bool preferred;
9913931579SAdam Jackson 	int modes;
10013931579SAdam Jackson };
101f453ba04SDave Airlie 
1025c61259eSZhao Yakui #define LEVEL_DMT	0
1035c61259eSZhao Yakui #define LEVEL_GTF	1
1047a374350SAdam Jackson #define LEVEL_GTF2	2
1057a374350SAdam Jackson #define LEVEL_CVT	3
1065c61259eSZhao Yakui 
1077d1be0a0SDouglas Anderson #define EDID_QUIRK(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _quirks) \
108e8de4d55SDouglas Anderson { \
1097d1be0a0SDouglas Anderson 	.panel_id = drm_edid_encode_panel_id(vend_chr_0, vend_chr_1, vend_chr_2, \
1107d1be0a0SDouglas Anderson 					     product_id), \
111e8de4d55SDouglas Anderson 	.quirks = _quirks \
112e8de4d55SDouglas Anderson }
113e8de4d55SDouglas Anderson 
11423c4cfbdSJani Nikula static const struct edid_quirk {
115e8de4d55SDouglas Anderson 	u32 panel_id;
116f453ba04SDave Airlie 	u32 quirks;
117f453ba04SDave Airlie } edid_quirk_list[] = {
118f453ba04SDave Airlie 	/* Acer AL1706 */
1197d1be0a0SDouglas Anderson 	EDID_QUIRK('A', 'C', 'R', 44358, EDID_QUIRK_PREFER_LARGE_60),
120f453ba04SDave Airlie 	/* Acer F51 */
1217d1be0a0SDouglas Anderson 	EDID_QUIRK('A', 'P', 'I', 0x7602, EDID_QUIRK_PREFER_LARGE_60),
122f453ba04SDave Airlie 
123e10aec65SMario Kleiner 	/* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
1247d1be0a0SDouglas Anderson 	EDID_QUIRK('A', 'E', 'O', 0, EDID_QUIRK_FORCE_6BPC),
125e10aec65SMario Kleiner 
12688630e91SHamza Mahfooz 	/* BenQ GW2765 */
12788630e91SHamza Mahfooz 	EDID_QUIRK('B', 'N', 'Q', 0x78d6, EDID_QUIRK_FORCE_8BPC),
12888630e91SHamza Mahfooz 
1290711a43bSKai-Heng Feng 	/* BOE model on HP Pavilion 15-n233sl reports 8 bpc, but is a 6 bpc panel */
1307d1be0a0SDouglas Anderson 	EDID_QUIRK('B', 'O', 'E', 0x78b, EDID_QUIRK_FORCE_6BPC),
1310711a43bSKai-Heng Feng 
13206998a75SKai-Heng Feng 	/* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */
1337d1be0a0SDouglas Anderson 	EDID_QUIRK('C', 'P', 'T', 0x17df, EDID_QUIRK_FORCE_6BPC),
13406998a75SKai-Heng Feng 
13525da7504SKai-Heng Feng 	/* SDC panel of Lenovo B50-80 reports 8 bpc, but is a 6 bpc panel */
1367d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'D', 'C', 0x3652, EDID_QUIRK_FORCE_6BPC),
13725da7504SKai-Heng Feng 
138922dceffSLee, Shawn C 	/* BOE model 0x0771 reports 8 bpc, but is a 6 bpc panel */
1397d1be0a0SDouglas Anderson 	EDID_QUIRK('B', 'O', 'E', 0x0771, EDID_QUIRK_FORCE_6BPC),
140922dceffSLee, Shawn C 
141f453ba04SDave Airlie 	/* Belinea 10 15 55 */
1427d1be0a0SDouglas Anderson 	EDID_QUIRK('M', 'A', 'X', 1516, EDID_QUIRK_PREFER_LARGE_60),
1437d1be0a0SDouglas Anderson 	EDID_QUIRK('M', 'A', 'X', 0x77e, EDID_QUIRK_PREFER_LARGE_60),
144f453ba04SDave Airlie 
145f453ba04SDave Airlie 	/* Envision Peripherals, Inc. EN-7100e */
1467d1be0a0SDouglas Anderson 	EDID_QUIRK('E', 'P', 'I', 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH),
147ba1163deSAdam Jackson 	/* Envision EN2028 */
1487d1be0a0SDouglas Anderson 	EDID_QUIRK('E', 'P', 'I', 8232, EDID_QUIRK_PREFER_LARGE_60),
149f453ba04SDave Airlie 
150f453ba04SDave Airlie 	/* Funai Electronics PM36B */
1517d1be0a0SDouglas Anderson 	EDID_QUIRK('F', 'C', 'M', 13600, EDID_QUIRK_PREFER_LARGE_75 |
152e8de4d55SDouglas Anderson 				       EDID_QUIRK_DETAILED_IN_CM),
153f453ba04SDave Airlie 
154aa193f7eSHamza Mahfooz 	/* LG 27GP950 */
155aa193f7eSHamza Mahfooz 	EDID_QUIRK('G', 'S', 'M', 0x5bbf, EDID_QUIRK_CAP_DSC_15BPP),
156aa193f7eSHamza Mahfooz 
157aa193f7eSHamza Mahfooz 	/* LG 27GN950 */
158aa193f7eSHamza Mahfooz 	EDID_QUIRK('G', 'S', 'M', 0x5b9a, EDID_QUIRK_CAP_DSC_15BPP),
159aa193f7eSHamza Mahfooz 
160e345da82SMario Kleiner 	/* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */
1617d1be0a0SDouglas Anderson 	EDID_QUIRK('L', 'G', 'D', 764, EDID_QUIRK_FORCE_10BPC),
162e345da82SMario Kleiner 
163f453ba04SDave Airlie 	/* LG Philips LCD LP154W01-A5 */
1647d1be0a0SDouglas Anderson 	EDID_QUIRK('L', 'P', 'L', 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE),
1657d1be0a0SDouglas Anderson 	EDID_QUIRK('L', 'P', 'L', 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE),
166f453ba04SDave Airlie 
167f453ba04SDave Airlie 	/* Samsung SyncMaster 205BW.  Note: irony */
1687d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'A', 'M', 541, EDID_QUIRK_DETAILED_SYNC_PP),
169f453ba04SDave Airlie 	/* Samsung SyncMaster 22[5-6]BW */
1707d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'A', 'M', 596, EDID_QUIRK_PREFER_LARGE_60),
1717d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'A', 'M', 638, EDID_QUIRK_PREFER_LARGE_60),
172bc42aabcSAdam Jackson 
173bc5b9641SMario Kleiner 	/* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
1747d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'N', 'Y', 0x2541, EDID_QUIRK_FORCE_12BPC),
175bc5b9641SMario Kleiner 
176bc42aabcSAdam Jackson 	/* ViewSonic VA2026w */
1777d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'S', 'C', 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING),
178118bdbd8SAlex Deucher 
179118bdbd8SAlex Deucher 	/* Medion MD 30217 PG */
1807d1be0a0SDouglas Anderson 	EDID_QUIRK('M', 'E', 'D', 0x7b8, EDID_QUIRK_PREFER_LARGE_75),
18149d45a31SRafał Miłecki 
18211bcf5f7SKai-Heng Feng 	/* Lenovo G50 */
1837d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'D', 'C', 18514, EDID_QUIRK_FORCE_6BPC),
18411bcf5f7SKai-Heng Feng 
18549d45a31SRafał Miłecki 	/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
1867d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'E', 'C', 0xd033, EDID_QUIRK_FORCE_8BPC),
18736fc5797STomeu Vizoso 
18836fc5797STomeu Vizoso 	/* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
1897d1be0a0SDouglas Anderson 	EDID_QUIRK('E', 'T', 'R', 13896, EDID_QUIRK_FORCE_8BPC),
190acb1d8eeSDave Airlie 
19130d62d44SAndres Rodriguez 	/* Valve Index Headset */
1927d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91a8, EDID_QUIRK_NON_DESKTOP),
1937d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b0, EDID_QUIRK_NON_DESKTOP),
1947d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b1, EDID_QUIRK_NON_DESKTOP),
1957d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b2, EDID_QUIRK_NON_DESKTOP),
1967d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b3, EDID_QUIRK_NON_DESKTOP),
1977d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b4, EDID_QUIRK_NON_DESKTOP),
1987d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b5, EDID_QUIRK_NON_DESKTOP),
1997d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b6, EDID_QUIRK_NON_DESKTOP),
2007d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b7, EDID_QUIRK_NON_DESKTOP),
2017d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b8, EDID_QUIRK_NON_DESKTOP),
2027d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91b9, EDID_QUIRK_NON_DESKTOP),
2037d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91ba, EDID_QUIRK_NON_DESKTOP),
2047d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91bb, EDID_QUIRK_NON_DESKTOP),
2057d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91bc, EDID_QUIRK_NON_DESKTOP),
2067d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91bd, EDID_QUIRK_NON_DESKTOP),
2077d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91be, EDID_QUIRK_NON_DESKTOP),
2087d1be0a0SDouglas Anderson 	EDID_QUIRK('V', 'L', 'V', 0x91bf, EDID_QUIRK_NON_DESKTOP),
20930d62d44SAndres Rodriguez 
2106931317cSLubosz Sarnecki 	/* HTC Vive and Vive Pro VR Headsets */
2117d1be0a0SDouglas Anderson 	EDID_QUIRK('H', 'V', 'R', 0xaa01, EDID_QUIRK_NON_DESKTOP),
2127d1be0a0SDouglas Anderson 	EDID_QUIRK('H', 'V', 'R', 0xaa02, EDID_QUIRK_NON_DESKTOP),
213b3b12ea3SPhilipp Zabel 
2145a3f6108SJan Schmidt 	/* Oculus Rift DK1, DK2, CV1 and Rift S VR Headsets */
2157d1be0a0SDouglas Anderson 	EDID_QUIRK('O', 'V', 'R', 0x0001, EDID_QUIRK_NON_DESKTOP),
2167d1be0a0SDouglas Anderson 	EDID_QUIRK('O', 'V', 'R', 0x0003, EDID_QUIRK_NON_DESKTOP),
2177d1be0a0SDouglas Anderson 	EDID_QUIRK('O', 'V', 'R', 0x0004, EDID_QUIRK_NON_DESKTOP),
2187d1be0a0SDouglas Anderson 	EDID_QUIRK('O', 'V', 'R', 0x0012, EDID_QUIRK_NON_DESKTOP),
21990eda8fcSPhilipp Zabel 
22090eda8fcSPhilipp Zabel 	/* Windows Mixed Reality Headsets */
2217d1be0a0SDouglas Anderson 	EDID_QUIRK('A', 'C', 'R', 0x7fce, EDID_QUIRK_NON_DESKTOP),
2227d1be0a0SDouglas Anderson 	EDID_QUIRK('L', 'E', 'N', 0x0408, EDID_QUIRK_NON_DESKTOP),
2237d1be0a0SDouglas Anderson 	EDID_QUIRK('F', 'U', 'J', 0x1970, EDID_QUIRK_NON_DESKTOP),
2247d1be0a0SDouglas Anderson 	EDID_QUIRK('D', 'E', 'L', 0x7fce, EDID_QUIRK_NON_DESKTOP),
2257d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'E', 'C', 0x144a, EDID_QUIRK_NON_DESKTOP),
2267d1be0a0SDouglas Anderson 	EDID_QUIRK('A', 'U', 'S', 0xc102, EDID_QUIRK_NON_DESKTOP),
227ccffc9ebSPhilipp Zabel 
228ccffc9ebSPhilipp Zabel 	/* Sony PlayStation VR Headset */
2297d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'N', 'Y', 0x0704, EDID_QUIRK_NON_DESKTOP),
23029054230SRyan Pavlik 
23129054230SRyan Pavlik 	/* Sensics VR Headsets */
2327d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'E', 'N', 0x1019, EDID_QUIRK_NON_DESKTOP),
23329054230SRyan Pavlik 
23429054230SRyan Pavlik 	/* OSVR HDK and HDK2 VR Headsets */
2357d1be0a0SDouglas Anderson 	EDID_QUIRK('S', 'V', 'R', 0x1019, EDID_QUIRK_NON_DESKTOP),
23698d4cb70SRalph Campbell 	EDID_QUIRK('A', 'U', 'O', 0x1111, EDID_QUIRK_NON_DESKTOP),
237f453ba04SDave Airlie };
238f453ba04SDave Airlie 
239a6b21831SThierry Reding /*
240a6b21831SThierry Reding  * Autogenerated from the DMT spec.
241a6b21831SThierry Reding  * This table is copied from xfree86/modes/xf86EdidModes.c.
242a6b21831SThierry Reding  */
243a6b21831SThierry Reding static const struct drm_display_mode drm_dmt_modes[] = {
24424b856b1SVille Syrjälä 	/* 0x01 - 640x350@85Hz */
245a6b21831SThierry Reding 	{ DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
246a6b21831SThierry Reding 		   736, 832, 0, 350, 382, 385, 445, 0,
247a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
24824b856b1SVille Syrjälä 	/* 0x02 - 640x400@85Hz */
249a6b21831SThierry Reding 	{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
250a6b21831SThierry Reding 		   736, 832, 0, 400, 401, 404, 445, 0,
251a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
25224b856b1SVille Syrjälä 	/* 0x03 - 720x400@85Hz */
253a6b21831SThierry Reding 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
254a6b21831SThierry Reding 		   828, 936, 0, 400, 401, 404, 446, 0,
255a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
25624b856b1SVille Syrjälä 	/* 0x04 - 640x480@60Hz */
257a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
258fcf22d05SVille Syrjälä 		   752, 800, 0, 480, 490, 492, 525, 0,
259a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26024b856b1SVille Syrjälä 	/* 0x05 - 640x480@72Hz */
261a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
262a6b21831SThierry Reding 		   704, 832, 0, 480, 489, 492, 520, 0,
263a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26424b856b1SVille Syrjälä 	/* 0x06 - 640x480@75Hz */
265a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
266a6b21831SThierry Reding 		   720, 840, 0, 480, 481, 484, 500, 0,
267a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26824b856b1SVille Syrjälä 	/* 0x07 - 640x480@85Hz */
269a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
270a6b21831SThierry Reding 		   752, 832, 0, 480, 481, 484, 509, 0,
271a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
27224b856b1SVille Syrjälä 	/* 0x08 - 800x600@56Hz */
273a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
274a6b21831SThierry Reding 		   896, 1024, 0, 600, 601, 603, 625, 0,
275a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
27624b856b1SVille Syrjälä 	/* 0x09 - 800x600@60Hz */
277a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
278a6b21831SThierry Reding 		   968, 1056, 0, 600, 601, 605, 628, 0,
279a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28024b856b1SVille Syrjälä 	/* 0x0a - 800x600@72Hz */
281a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
282a6b21831SThierry Reding 		   976, 1040, 0, 600, 637, 643, 666, 0,
283a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28424b856b1SVille Syrjälä 	/* 0x0b - 800x600@75Hz */
285a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
286a6b21831SThierry Reding 		   896, 1056, 0, 600, 601, 604, 625, 0,
287a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28824b856b1SVille Syrjälä 	/* 0x0c - 800x600@85Hz */
289a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
290a6b21831SThierry Reding 		   896, 1048, 0, 600, 601, 604, 631, 0,
291a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
29224b856b1SVille Syrjälä 	/* 0x0d - 800x600@120Hz RB */
293a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848,
294a6b21831SThierry Reding 		   880, 960, 0, 600, 603, 607, 636, 0,
295a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
29624b856b1SVille Syrjälä 	/* 0x0e - 848x480@60Hz */
297a6b21831SThierry Reding 	{ DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
298a6b21831SThierry Reding 		   976, 1088, 0, 480, 486, 494, 517, 0,
299a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
30024b856b1SVille Syrjälä 	/* 0x0f - 1024x768@43Hz, interlace */
301a6b21831SThierry Reding 	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
302735b100fSPaul Parsons 		   1208, 1264, 0, 768, 768, 776, 817, 0,
303a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
304a6b21831SThierry Reding 		   DRM_MODE_FLAG_INTERLACE) },
30524b856b1SVille Syrjälä 	/* 0x10 - 1024x768@60Hz */
306a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
307a6b21831SThierry Reding 		   1184, 1344, 0, 768, 771, 777, 806, 0,
308a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
30924b856b1SVille Syrjälä 	/* 0x11 - 1024x768@70Hz */
310a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
311a6b21831SThierry Reding 		   1184, 1328, 0, 768, 771, 777, 806, 0,
312a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
31324b856b1SVille Syrjälä 	/* 0x12 - 1024x768@75Hz */
314a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
315a6b21831SThierry Reding 		   1136, 1312, 0, 768, 769, 772, 800, 0,
316a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
31724b856b1SVille Syrjälä 	/* 0x13 - 1024x768@85Hz */
318a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
319a6b21831SThierry Reding 		   1168, 1376, 0, 768, 769, 772, 808, 0,
320a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
32124b856b1SVille Syrjälä 	/* 0x14 - 1024x768@120Hz RB */
322a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072,
323a6b21831SThierry Reding 		   1104, 1184, 0, 768, 771, 775, 813, 0,
324a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
32524b856b1SVille Syrjälä 	/* 0x15 - 1152x864@75Hz */
326a6b21831SThierry Reding 	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
327a6b21831SThierry Reding 		   1344, 1600, 0, 864, 865, 868, 900, 0,
328a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
329bfcd74d2SVille Syrjälä 	/* 0x55 - 1280x720@60Hz */
330bfcd74d2SVille Syrjälä 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
331bfcd74d2SVille Syrjälä 		   1430, 1650, 0, 720, 725, 730, 750, 0,
332bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
33324b856b1SVille Syrjälä 	/* 0x16 - 1280x768@60Hz RB */
334a6b21831SThierry Reding 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328,
335a6b21831SThierry Reding 		   1360, 1440, 0, 768, 771, 778, 790, 0,
336a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
33724b856b1SVille Syrjälä 	/* 0x17 - 1280x768@60Hz */
338a6b21831SThierry Reding 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
339a6b21831SThierry Reding 		   1472, 1664, 0, 768, 771, 778, 798, 0,
340a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34124b856b1SVille Syrjälä 	/* 0x18 - 1280x768@75Hz */
342a6b21831SThierry Reding 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
343a6b21831SThierry Reding 		   1488, 1696, 0, 768, 771, 778, 805, 0,
344fcf22d05SVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34524b856b1SVille Syrjälä 	/* 0x19 - 1280x768@85Hz */
346a6b21831SThierry Reding 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
347a6b21831SThierry Reding 		   1496, 1712, 0, 768, 771, 778, 809, 0,
348a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34924b856b1SVille Syrjälä 	/* 0x1a - 1280x768@120Hz RB */
350a6b21831SThierry Reding 	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328,
351a6b21831SThierry Reding 		   1360, 1440, 0, 768, 771, 778, 813, 0,
352a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
35324b856b1SVille Syrjälä 	/* 0x1b - 1280x800@60Hz RB */
354a6b21831SThierry Reding 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328,
355a6b21831SThierry Reding 		   1360, 1440, 0, 800, 803, 809, 823, 0,
356a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
35724b856b1SVille Syrjälä 	/* 0x1c - 1280x800@60Hz */
358a6b21831SThierry Reding 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
359a6b21831SThierry Reding 		   1480, 1680, 0, 800, 803, 809, 831, 0,
360fcf22d05SVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36124b856b1SVille Syrjälä 	/* 0x1d - 1280x800@75Hz */
362a6b21831SThierry Reding 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
363a6b21831SThierry Reding 		   1488, 1696, 0, 800, 803, 809, 838, 0,
364a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36524b856b1SVille Syrjälä 	/* 0x1e - 1280x800@85Hz */
366a6b21831SThierry Reding 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
367a6b21831SThierry Reding 		   1496, 1712, 0, 800, 803, 809, 843, 0,
368a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36924b856b1SVille Syrjälä 	/* 0x1f - 1280x800@120Hz RB */
370a6b21831SThierry Reding 	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328,
371a6b21831SThierry Reding 		   1360, 1440, 0, 800, 803, 809, 847, 0,
372a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
37324b856b1SVille Syrjälä 	/* 0x20 - 1280x960@60Hz */
374a6b21831SThierry Reding 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
375a6b21831SThierry Reding 		   1488, 1800, 0, 960, 961, 964, 1000, 0,
376a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
37724b856b1SVille Syrjälä 	/* 0x21 - 1280x960@85Hz */
378a6b21831SThierry Reding 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
379a6b21831SThierry Reding 		   1504, 1728, 0, 960, 961, 964, 1011, 0,
380a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
38124b856b1SVille Syrjälä 	/* 0x22 - 1280x960@120Hz RB */
382a6b21831SThierry Reding 	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328,
383a6b21831SThierry Reding 		   1360, 1440, 0, 960, 963, 967, 1017, 0,
384a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
38524b856b1SVille Syrjälä 	/* 0x23 - 1280x1024@60Hz */
386a6b21831SThierry Reding 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
387a6b21831SThierry Reding 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
388a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
38924b856b1SVille Syrjälä 	/* 0x24 - 1280x1024@75Hz */
390a6b21831SThierry Reding 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
391a6b21831SThierry Reding 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
392a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
39324b856b1SVille Syrjälä 	/* 0x25 - 1280x1024@85Hz */
394a6b21831SThierry Reding 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
395a6b21831SThierry Reding 		   1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
396a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
39724b856b1SVille Syrjälä 	/* 0x26 - 1280x1024@120Hz RB */
398a6b21831SThierry Reding 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328,
399a6b21831SThierry Reding 		   1360, 1440, 0, 1024, 1027, 1034, 1084, 0,
400a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
40124b856b1SVille Syrjälä 	/* 0x27 - 1360x768@60Hz */
402a6b21831SThierry Reding 	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
403a6b21831SThierry Reding 		   1536, 1792, 0, 768, 771, 777, 795, 0,
404a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
40524b856b1SVille Syrjälä 	/* 0x28 - 1360x768@120Hz RB */
406a6b21831SThierry Reding 	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408,
407a6b21831SThierry Reding 		   1440, 1520, 0, 768, 771, 776, 813, 0,
408a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
409bfcd74d2SVille Syrjälä 	/* 0x51 - 1366x768@60Hz */
410bfcd74d2SVille Syrjälä 	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 85500, 1366, 1436,
411bfcd74d2SVille Syrjälä 		   1579, 1792, 0, 768, 771, 774, 798, 0,
412bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
413bfcd74d2SVille Syrjälä 	/* 0x56 - 1366x768@60Hz */
414bfcd74d2SVille Syrjälä 	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 72000, 1366, 1380,
415bfcd74d2SVille Syrjälä 		   1436, 1500, 0, 768, 769, 772, 800, 0,
416bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
41724b856b1SVille Syrjälä 	/* 0x29 - 1400x1050@60Hz RB */
418a6b21831SThierry Reding 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448,
419a6b21831SThierry Reding 		   1480, 1560, 0, 1050, 1053, 1057, 1080, 0,
420a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
42124b856b1SVille Syrjälä 	/* 0x2a - 1400x1050@60Hz */
422a6b21831SThierry Reding 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
423a6b21831SThierry Reding 		   1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
424a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
42524b856b1SVille Syrjälä 	/* 0x2b - 1400x1050@75Hz */
426a6b21831SThierry Reding 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
427a6b21831SThierry Reding 		   1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
428a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
42924b856b1SVille Syrjälä 	/* 0x2c - 1400x1050@85Hz */
430a6b21831SThierry Reding 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
431a6b21831SThierry Reding 		   1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
432a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
43324b856b1SVille Syrjälä 	/* 0x2d - 1400x1050@120Hz RB */
434a6b21831SThierry Reding 	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448,
435a6b21831SThierry Reding 		   1480, 1560, 0, 1050, 1053, 1057, 1112, 0,
436a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
43724b856b1SVille Syrjälä 	/* 0x2e - 1440x900@60Hz RB */
438a6b21831SThierry Reding 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488,
439a6b21831SThierry Reding 		   1520, 1600, 0, 900, 903, 909, 926, 0,
440a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
44124b856b1SVille Syrjälä 	/* 0x2f - 1440x900@60Hz */
442a6b21831SThierry Reding 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
443a6b21831SThierry Reding 		   1672, 1904, 0, 900, 903, 909, 934, 0,
444a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44524b856b1SVille Syrjälä 	/* 0x30 - 1440x900@75Hz */
446a6b21831SThierry Reding 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
447a6b21831SThierry Reding 		   1688, 1936, 0, 900, 903, 909, 942, 0,
448a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44924b856b1SVille Syrjälä 	/* 0x31 - 1440x900@85Hz */
450a6b21831SThierry Reding 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
451a6b21831SThierry Reding 		   1696, 1952, 0, 900, 903, 909, 948, 0,
452a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
45324b856b1SVille Syrjälä 	/* 0x32 - 1440x900@120Hz RB */
454a6b21831SThierry Reding 	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488,
455a6b21831SThierry Reding 		   1520, 1600, 0, 900, 903, 909, 953, 0,
456a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
457bfcd74d2SVille Syrjälä 	/* 0x53 - 1600x900@60Hz */
458bfcd74d2SVille Syrjälä 	{ DRM_MODE("1600x900", DRM_MODE_TYPE_DRIVER, 108000, 1600, 1624,
459bfcd74d2SVille Syrjälä 		   1704, 1800, 0, 900, 901, 904, 1000, 0,
460bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46124b856b1SVille Syrjälä 	/* 0x33 - 1600x1200@60Hz */
462a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
463a6b21831SThierry Reding 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
464a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46524b856b1SVille Syrjälä 	/* 0x34 - 1600x1200@65Hz */
466a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
467a6b21831SThierry Reding 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
468a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46924b856b1SVille Syrjälä 	/* 0x35 - 1600x1200@70Hz */
470a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
471a6b21831SThierry Reding 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
472a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
47324b856b1SVille Syrjälä 	/* 0x36 - 1600x1200@75Hz */
474a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
475a6b21831SThierry Reding 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
476a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
47724b856b1SVille Syrjälä 	/* 0x37 - 1600x1200@85Hz */
478a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
479a6b21831SThierry Reding 		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
480a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
48124b856b1SVille Syrjälä 	/* 0x38 - 1600x1200@120Hz RB */
482a6b21831SThierry Reding 	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648,
483a6b21831SThierry Reding 		   1680, 1760, 0, 1200, 1203, 1207, 1271, 0,
484a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
48524b856b1SVille Syrjälä 	/* 0x39 - 1680x1050@60Hz RB */
486a6b21831SThierry Reding 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728,
487a6b21831SThierry Reding 		   1760, 1840, 0, 1050, 1053, 1059, 1080, 0,
488a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
48924b856b1SVille Syrjälä 	/* 0x3a - 1680x1050@60Hz */
490a6b21831SThierry Reding 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
491a6b21831SThierry Reding 		   1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
492a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49324b856b1SVille Syrjälä 	/* 0x3b - 1680x1050@75Hz */
494a6b21831SThierry Reding 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
495a6b21831SThierry Reding 		   1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
496a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49724b856b1SVille Syrjälä 	/* 0x3c - 1680x1050@85Hz */
498a6b21831SThierry Reding 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
499a6b21831SThierry Reding 		   1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
500a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
50124b856b1SVille Syrjälä 	/* 0x3d - 1680x1050@120Hz RB */
502a6b21831SThierry Reding 	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728,
503a6b21831SThierry Reding 		   1760, 1840, 0, 1050, 1053, 1059, 1112, 0,
504a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
50524b856b1SVille Syrjälä 	/* 0x3e - 1792x1344@60Hz */
506a6b21831SThierry Reding 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
507a6b21831SThierry Reding 		   2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
508a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
50924b856b1SVille Syrjälä 	/* 0x3f - 1792x1344@75Hz */
510a6b21831SThierry Reding 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
511a6b21831SThierry Reding 		   2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
512a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
51324b856b1SVille Syrjälä 	/* 0x40 - 1792x1344@120Hz RB */
514a6b21831SThierry Reding 	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840,
515a6b21831SThierry Reding 		   1872, 1952, 0, 1344, 1347, 1351, 1423, 0,
516a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
51724b856b1SVille Syrjälä 	/* 0x41 - 1856x1392@60Hz */
518a6b21831SThierry Reding 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
519a6b21831SThierry Reding 		   2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
520a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
52124b856b1SVille Syrjälä 	/* 0x42 - 1856x1392@75Hz */
522a6b21831SThierry Reding 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
523fcf22d05SVille Syrjälä 		   2208, 2560, 0, 1392, 1393, 1396, 1500, 0,
524a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
52524b856b1SVille Syrjälä 	/* 0x43 - 1856x1392@120Hz RB */
526a6b21831SThierry Reding 	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904,
527a6b21831SThierry Reding 		   1936, 2016, 0, 1392, 1395, 1399, 1474, 0,
528a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
529bfcd74d2SVille Syrjälä 	/* 0x52 - 1920x1080@60Hz */
530bfcd74d2SVille Syrjälä 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
531bfcd74d2SVille Syrjälä 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
532bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
53324b856b1SVille Syrjälä 	/* 0x44 - 1920x1200@60Hz RB */
534a6b21831SThierry Reding 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968,
535a6b21831SThierry Reding 		   2000, 2080, 0, 1200, 1203, 1209, 1235, 0,
536a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
53724b856b1SVille Syrjälä 	/* 0x45 - 1920x1200@60Hz */
538a6b21831SThierry Reding 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
539a6b21831SThierry Reding 		   2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
540a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54124b856b1SVille Syrjälä 	/* 0x46 - 1920x1200@75Hz */
542a6b21831SThierry Reding 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
543a6b21831SThierry Reding 		   2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
544a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54524b856b1SVille Syrjälä 	/* 0x47 - 1920x1200@85Hz */
546a6b21831SThierry Reding 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
547a6b21831SThierry Reding 		   2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
548a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54924b856b1SVille Syrjälä 	/* 0x48 - 1920x1200@120Hz RB */
550a6b21831SThierry Reding 	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968,
551a6b21831SThierry Reding 		   2000, 2080, 0, 1200, 1203, 1209, 1271, 0,
552a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
55324b856b1SVille Syrjälä 	/* 0x49 - 1920x1440@60Hz */
554a6b21831SThierry Reding 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
555a6b21831SThierry Reding 		   2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
556a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
55724b856b1SVille Syrjälä 	/* 0x4a - 1920x1440@75Hz */
558a6b21831SThierry Reding 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
559a6b21831SThierry Reding 		   2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
560a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
56124b856b1SVille Syrjälä 	/* 0x4b - 1920x1440@120Hz RB */
562a6b21831SThierry Reding 	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968,
563a6b21831SThierry Reding 		   2000, 2080, 0, 1440, 1443, 1447, 1525, 0,
564a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
565bfcd74d2SVille Syrjälä 	/* 0x54 - 2048x1152@60Hz */
566bfcd74d2SVille Syrjälä 	{ DRM_MODE("2048x1152", DRM_MODE_TYPE_DRIVER, 162000, 2048, 2074,
567bfcd74d2SVille Syrjälä 		   2154, 2250, 0, 1152, 1153, 1156, 1200, 0,
568bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
56924b856b1SVille Syrjälä 	/* 0x4c - 2560x1600@60Hz RB */
570a6b21831SThierry Reding 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608,
571a6b21831SThierry Reding 		   2640, 2720, 0, 1600, 1603, 1609, 1646, 0,
572a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
57324b856b1SVille Syrjälä 	/* 0x4d - 2560x1600@60Hz */
574a6b21831SThierry Reding 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
575a6b21831SThierry Reding 		   3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
576a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
57724b856b1SVille Syrjälä 	/* 0x4e - 2560x1600@75Hz */
578a6b21831SThierry Reding 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
579a6b21831SThierry Reding 		   3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
580a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
58124b856b1SVille Syrjälä 	/* 0x4f - 2560x1600@85Hz */
582a6b21831SThierry Reding 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
583a6b21831SThierry Reding 		   3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
584a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
58524b856b1SVille Syrjälä 	/* 0x50 - 2560x1600@120Hz RB */
586a6b21831SThierry Reding 	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608,
587a6b21831SThierry Reding 		   2640, 2720, 0, 1600, 1603, 1609, 1694, 0,
588a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
589bfcd74d2SVille Syrjälä 	/* 0x57 - 4096x2160@60Hz RB */
590bfcd74d2SVille Syrjälä 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556744, 4096, 4104,
591bfcd74d2SVille Syrjälä 		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
592bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
593bfcd74d2SVille Syrjälä 	/* 0x58 - 4096x2160@59.94Hz RB */
594bfcd74d2SVille Syrjälä 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104,
595bfcd74d2SVille Syrjälä 		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
596bfcd74d2SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
597a6b21831SThierry Reding };
598a6b21831SThierry Reding 
599e7bfa5c4SVille Syrjälä /*
600e7bfa5c4SVille Syrjälä  * These more or less come from the DMT spec.  The 720x400 modes are
601e7bfa5c4SVille Syrjälä  * inferred from historical 80x25 practice.  The 640x480@67 and 832x624@75
602e7bfa5c4SVille Syrjälä  * modes are old-school Mac modes.  The EDID spec says the 1152x864@75 mode
603e7bfa5c4SVille Syrjälä  * should be 1152x870, again for the Mac, but instead we use the x864 DMT
604e7bfa5c4SVille Syrjälä  * mode.
605e7bfa5c4SVille Syrjälä  *
606e7bfa5c4SVille Syrjälä  * The DMT modes have been fact-checked; the rest are mild guesses.
607e7bfa5c4SVille Syrjälä  */
608a6b21831SThierry Reding static const struct drm_display_mode edid_est_modes[] = {
609a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
610a6b21831SThierry Reding 		   968, 1056, 0, 600, 601, 605, 628, 0,
611a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
612a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
613a6b21831SThierry Reding 		   896, 1024, 0, 600, 601, 603,  625, 0,
614a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
615a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
616a6b21831SThierry Reding 		   720, 840, 0, 480, 481, 484, 500, 0,
617a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
618a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
61987707cfdSPaul Parsons 		   704,  832, 0, 480, 489, 492, 520, 0,
620a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
621a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
622a6b21831SThierry Reding 		   768,  864, 0, 480, 483, 486, 525, 0,
623a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
62487707cfdSPaul Parsons 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
625a6b21831SThierry Reding 		   752, 800, 0, 480, 490, 492, 525, 0,
626a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
627a6b21831SThierry Reding 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
628a6b21831SThierry Reding 		   846, 900, 0, 400, 421, 423,  449, 0,
629a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
630a6b21831SThierry Reding 	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
631a6b21831SThierry Reding 		   846,  900, 0, 400, 412, 414, 449, 0,
632a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
633a6b21831SThierry Reding 	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
634a6b21831SThierry Reding 		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
635a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
63687707cfdSPaul Parsons 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
637a6b21831SThierry Reding 		   1136, 1312, 0,  768, 769, 772, 800, 0,
638a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
639a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
640a6b21831SThierry Reding 		   1184, 1328, 0,  768, 771, 777, 806, 0,
641a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
642a6b21831SThierry Reding 	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
643a6b21831SThierry Reding 		   1184, 1344, 0,  768, 771, 777, 806, 0,
644a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
645a6b21831SThierry Reding 	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
646a6b21831SThierry Reding 		   1208, 1264, 0, 768, 768, 776, 817, 0,
647a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
648a6b21831SThierry Reding 	{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
649a6b21831SThierry Reding 		   928, 1152, 0, 624, 625, 628, 667, 0,
650a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
651a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
652a6b21831SThierry Reding 		   896, 1056, 0, 600, 601, 604,  625, 0,
653a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
654a6b21831SThierry Reding 	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
655a6b21831SThierry Reding 		   976, 1040, 0, 600, 637, 643, 666, 0,
656a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
657a6b21831SThierry Reding 	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
658a6b21831SThierry Reding 		   1344, 1600, 0,  864, 865, 868, 900, 0,
659a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
660a6b21831SThierry Reding };
661a6b21831SThierry Reding 
662a6b21831SThierry Reding struct minimode {
663a6b21831SThierry Reding 	short w;
664a6b21831SThierry Reding 	short h;
665a6b21831SThierry Reding 	short r;
666a6b21831SThierry Reding 	short rb;
667a6b21831SThierry Reding };
668a6b21831SThierry Reding 
669a6b21831SThierry Reding static const struct minimode est3_modes[] = {
670a6b21831SThierry Reding 	/* byte 6 */
671a6b21831SThierry Reding 	{ 640, 350, 85, 0 },
672a6b21831SThierry Reding 	{ 640, 400, 85, 0 },
673a6b21831SThierry Reding 	{ 720, 400, 85, 0 },
674a6b21831SThierry Reding 	{ 640, 480, 85, 0 },
675a6b21831SThierry Reding 	{ 848, 480, 60, 0 },
676a6b21831SThierry Reding 	{ 800, 600, 85, 0 },
677a6b21831SThierry Reding 	{ 1024, 768, 85, 0 },
678a6b21831SThierry Reding 	{ 1152, 864, 75, 0 },
679a6b21831SThierry Reding 	/* byte 7 */
680a6b21831SThierry Reding 	{ 1280, 768, 60, 1 },
681a6b21831SThierry Reding 	{ 1280, 768, 60, 0 },
682a6b21831SThierry Reding 	{ 1280, 768, 75, 0 },
683a6b21831SThierry Reding 	{ 1280, 768, 85, 0 },
684a6b21831SThierry Reding 	{ 1280, 960, 60, 0 },
685a6b21831SThierry Reding 	{ 1280, 960, 85, 0 },
686a6b21831SThierry Reding 	{ 1280, 1024, 60, 0 },
687a6b21831SThierry Reding 	{ 1280, 1024, 85, 0 },
688a6b21831SThierry Reding 	/* byte 8 */
689a6b21831SThierry Reding 	{ 1360, 768, 60, 0 },
690a6b21831SThierry Reding 	{ 1440, 900, 60, 1 },
691a6b21831SThierry Reding 	{ 1440, 900, 60, 0 },
692a6b21831SThierry Reding 	{ 1440, 900, 75, 0 },
693a6b21831SThierry Reding 	{ 1440, 900, 85, 0 },
694a6b21831SThierry Reding 	{ 1400, 1050, 60, 1 },
695a6b21831SThierry Reding 	{ 1400, 1050, 60, 0 },
696a6b21831SThierry Reding 	{ 1400, 1050, 75, 0 },
697a6b21831SThierry Reding 	/* byte 9 */
698a6b21831SThierry Reding 	{ 1400, 1050, 85, 0 },
699a6b21831SThierry Reding 	{ 1680, 1050, 60, 1 },
700a6b21831SThierry Reding 	{ 1680, 1050, 60, 0 },
701a6b21831SThierry Reding 	{ 1680, 1050, 75, 0 },
702a6b21831SThierry Reding 	{ 1680, 1050, 85, 0 },
703a6b21831SThierry Reding 	{ 1600, 1200, 60, 0 },
704a6b21831SThierry Reding 	{ 1600, 1200, 65, 0 },
705a6b21831SThierry Reding 	{ 1600, 1200, 70, 0 },
706a6b21831SThierry Reding 	/* byte 10 */
707a6b21831SThierry Reding 	{ 1600, 1200, 75, 0 },
708a6b21831SThierry Reding 	{ 1600, 1200, 85, 0 },
709a6b21831SThierry Reding 	{ 1792, 1344, 60, 0 },
710c068b32aSVille Syrjälä 	{ 1792, 1344, 75, 0 },
711a6b21831SThierry Reding 	{ 1856, 1392, 60, 0 },
712a6b21831SThierry Reding 	{ 1856, 1392, 75, 0 },
713a6b21831SThierry Reding 	{ 1920, 1200, 60, 1 },
714a6b21831SThierry Reding 	{ 1920, 1200, 60, 0 },
715a6b21831SThierry Reding 	/* byte 11 */
716a6b21831SThierry Reding 	{ 1920, 1200, 75, 0 },
717a6b21831SThierry Reding 	{ 1920, 1200, 85, 0 },
718a6b21831SThierry Reding 	{ 1920, 1440, 60, 0 },
719a6b21831SThierry Reding 	{ 1920, 1440, 75, 0 },
720a6b21831SThierry Reding };
721a6b21831SThierry Reding 
722a6b21831SThierry Reding static const struct minimode extra_modes[] = {
723a6b21831SThierry Reding 	{ 1024, 576,  60, 0 },
724a6b21831SThierry Reding 	{ 1366, 768,  60, 0 },
725a6b21831SThierry Reding 	{ 1600, 900,  60, 0 },
726a6b21831SThierry Reding 	{ 1680, 945,  60, 0 },
727a6b21831SThierry Reding 	{ 1920, 1080, 60, 0 },
728a6b21831SThierry Reding 	{ 2048, 1152, 60, 0 },
729a6b21831SThierry Reding 	{ 2048, 1536, 60, 0 },
730a6b21831SThierry Reding };
731a6b21831SThierry Reding 
732a6b21831SThierry Reding /*
7337befe621SVille Syrjälä  * From CEA/CTA-861 spec.
734d9278b4cSJani Nikula  *
7357befe621SVille Syrjälä  * Do not access directly, instead always use cea_mode_for_vic().
736a6b21831SThierry Reding  */
7378c1b2bd9SVille Syrjälä static const struct drm_display_mode edid_cea_modes_1[] = {
73878691960SVille Syrjälä 	/* 1 - 640x480@60Hz 4:3 */
739a6b21831SThierry Reding 	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
740a6b21831SThierry Reding 		   752, 800, 0, 480, 490, 492, 525, 0,
741ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7420425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
74378691960SVille Syrjälä 	/* 2 - 720x480@60Hz 4:3 */
744a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
745a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
746ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7470425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
74878691960SVille Syrjälä 	/* 3 - 720x480@60Hz 16:9 */
749a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
750a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
751ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
7520425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
75378691960SVille Syrjälä 	/* 4 - 1280x720@60Hz 16:9 */
754a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
755a6b21831SThierry Reding 		   1430, 1650, 0, 720, 725, 730, 750, 0,
756ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
7570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
75878691960SVille Syrjälä 	/* 5 - 1920x1080i@60Hz 16:9 */
759a6b21831SThierry Reding 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
760a6b21831SThierry Reding 		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
761a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
762ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
7630425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
76478691960SVille Syrjälä 	/* 6 - 720(1440)x480i@60Hz 4:3 */
765fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
766fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
767a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
768ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7690425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
77078691960SVille Syrjälä 	/* 7 - 720(1440)x480i@60Hz 16:9 */
771fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
772fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
773a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
774ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
7750425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
77678691960SVille Syrjälä 	/* 8 - 720(1440)x240@60Hz 4:3 */
777fb01d280SClint Taylor 	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
778fb01d280SClint Taylor 		   801, 858, 0, 240, 244, 247, 262, 0,
779a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
780ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_DBLCLK),
7810425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
78278691960SVille Syrjälä 	/* 9 - 720(1440)x240@60Hz 16:9 */
783fb01d280SClint Taylor 	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
784fb01d280SClint Taylor 		   801, 858, 0, 240, 244, 247, 262, 0,
785a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
786ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_DBLCLK),
7870425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
78878691960SVille Syrjälä 	/* 10 - 2880x480i@60Hz 4:3 */
789a6b21831SThierry Reding 	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
790a6b21831SThierry Reding 		   3204, 3432, 0, 480, 488, 494, 525, 0,
791a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
792ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
7930425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
79478691960SVille Syrjälä 	/* 11 - 2880x480i@60Hz 16:9 */
795a6b21831SThierry Reding 	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
796a6b21831SThierry Reding 		   3204, 3432, 0, 480, 488, 494, 525, 0,
797a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
798ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
7990425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
80078691960SVille Syrjälä 	/* 12 - 2880x240@60Hz 4:3 */
801a6b21831SThierry Reding 	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
802a6b21831SThierry Reding 		   3204, 3432, 0, 240, 244, 247, 262, 0,
803ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8040425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
80578691960SVille Syrjälä 	/* 13 - 2880x240@60Hz 16:9 */
806a6b21831SThierry Reding 	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
807a6b21831SThierry Reding 		   3204, 3432, 0, 240, 244, 247, 262, 0,
808ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8090425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
81078691960SVille Syrjälä 	/* 14 - 1440x480@60Hz 4:3 */
811a6b21831SThierry Reding 	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
812a6b21831SThierry Reding 		   1596, 1716, 0, 480, 489, 495, 525, 0,
813ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8140425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
81578691960SVille Syrjälä 	/* 15 - 1440x480@60Hz 16:9 */
816a6b21831SThierry Reding 	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
817a6b21831SThierry Reding 		   1596, 1716, 0, 480, 489, 495, 525, 0,
818ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8190425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
82078691960SVille Syrjälä 	/* 16 - 1920x1080@60Hz 16:9 */
821a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
822a6b21831SThierry Reding 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
823ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8240425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
82578691960SVille Syrjälä 	/* 17 - 720x576@50Hz 4:3 */
826a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
827a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
828ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8290425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
83078691960SVille Syrjälä 	/* 18 - 720x576@50Hz 16:9 */
831a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
832a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
833ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8340425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
83578691960SVille Syrjälä 	/* 19 - 1280x720@50Hz 16:9 */
836a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
837a6b21831SThierry Reding 		   1760, 1980, 0, 720, 725, 730, 750, 0,
838ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
8390425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
84078691960SVille Syrjälä 	/* 20 - 1920x1080i@50Hz 16:9 */
841a6b21831SThierry Reding 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
842a6b21831SThierry Reding 		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
843a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
844ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
8450425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
84678691960SVille Syrjälä 	/* 21 - 720(1440)x576i@50Hz 4:3 */
847fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
848fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
849a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
850ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
8510425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
85278691960SVille Syrjälä 	/* 22 - 720(1440)x576i@50Hz 16:9 */
853fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
854fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
855a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
856ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
8570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
85878691960SVille Syrjälä 	/* 23 - 720(1440)x288@50Hz 4:3 */
859fb01d280SClint Taylor 	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
860fb01d280SClint Taylor 		   795, 864, 0, 288, 290, 293, 312, 0,
861a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
862ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_DBLCLK),
8630425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
86478691960SVille Syrjälä 	/* 24 - 720(1440)x288@50Hz 16:9 */
865fb01d280SClint Taylor 	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
866fb01d280SClint Taylor 		   795, 864, 0, 288, 290, 293, 312, 0,
867a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
868ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_DBLCLK),
8690425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
87078691960SVille Syrjälä 	/* 25 - 2880x576i@50Hz 4:3 */
871a6b21831SThierry Reding 	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
872a6b21831SThierry Reding 		   3180, 3456, 0, 576, 580, 586, 625, 0,
873a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
874ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
8750425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
87678691960SVille Syrjälä 	/* 26 - 2880x576i@50Hz 16:9 */
877a6b21831SThierry Reding 	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
878a6b21831SThierry Reding 		   3180, 3456, 0, 576, 580, 586, 625, 0,
879a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
880ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
8810425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
88278691960SVille Syrjälä 	/* 27 - 2880x288@50Hz 4:3 */
883a6b21831SThierry Reding 	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
884a6b21831SThierry Reding 		   3180, 3456, 0, 288, 290, 293, 312, 0,
885ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8860425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
88778691960SVille Syrjälä 	/* 28 - 2880x288@50Hz 16:9 */
888a6b21831SThierry Reding 	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
889a6b21831SThierry Reding 		   3180, 3456, 0, 288, 290, 293, 312, 0,
890ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8910425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
89278691960SVille Syrjälä 	/* 29 - 1440x576@50Hz 4:3 */
893a6b21831SThierry Reding 	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
894a6b21831SThierry Reding 		   1592, 1728, 0, 576, 581, 586, 625, 0,
895ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
8960425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
89778691960SVille Syrjälä 	/* 30 - 1440x576@50Hz 16:9 */
898a6b21831SThierry Reding 	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
899a6b21831SThierry Reding 		   1592, 1728, 0, 576, 581, 586, 625, 0,
900ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9010425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
90278691960SVille Syrjälä 	/* 31 - 1920x1080@50Hz 16:9 */
903a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
904a6b21831SThierry Reding 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
905ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9060425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
90778691960SVille Syrjälä 	/* 32 - 1920x1080@24Hz 16:9 */
908a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
909a6b21831SThierry Reding 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
910ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9110425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
91278691960SVille Syrjälä 	/* 33 - 1920x1080@25Hz 16:9 */
913a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
914a6b21831SThierry Reding 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
915ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9160425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
91778691960SVille Syrjälä 	/* 34 - 1920x1080@30Hz 16:9 */
918a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
919a6b21831SThierry Reding 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
920ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9210425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
92278691960SVille Syrjälä 	/* 35 - 2880x480@60Hz 4:3 */
923a6b21831SThierry Reding 	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
924a6b21831SThierry Reding 		   3192, 3432, 0, 480, 489, 495, 525, 0,
925ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9260425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
92778691960SVille Syrjälä 	/* 36 - 2880x480@60Hz 16:9 */
928a6b21831SThierry Reding 	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
929a6b21831SThierry Reding 		   3192, 3432, 0, 480, 489, 495, 525, 0,
930ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9310425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
93278691960SVille Syrjälä 	/* 37 - 2880x576@50Hz 4:3 */
933a6b21831SThierry Reding 	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
934a6b21831SThierry Reding 		   3184, 3456, 0, 576, 581, 586, 625, 0,
935ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9360425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
93778691960SVille Syrjälä 	/* 38 - 2880x576@50Hz 16:9 */
938a6b21831SThierry Reding 	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
939a6b21831SThierry Reding 		   3184, 3456, 0, 576, 581, 586, 625, 0,
940ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9410425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
94278691960SVille Syrjälä 	/* 39 - 1920x1080i@50Hz 16:9 */
943a6b21831SThierry Reding 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
944a6b21831SThierry Reding 		   2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
945a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
946ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
9470425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
94878691960SVille Syrjälä 	/* 40 - 1920x1080i@100Hz 16:9 */
949a6b21831SThierry Reding 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
950a6b21831SThierry Reding 		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
951a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
952ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
9530425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
95478691960SVille Syrjälä 	/* 41 - 1280x720@100Hz 16:9 */
955a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
956a6b21831SThierry Reding 		   1760, 1980, 0, 720, 725, 730, 750, 0,
957ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9580425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
95978691960SVille Syrjälä 	/* 42 - 720x576@100Hz 4:3 */
960a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
961a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
962ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9630425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
96478691960SVille Syrjälä 	/* 43 - 720x576@100Hz 16:9 */
965a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
966a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
967ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9680425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
96978691960SVille Syrjälä 	/* 44 - 720(1440)x576i@100Hz 4:3 */
970fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
971fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
972a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9735a11f7f8SClint Taylor 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9740425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
97578691960SVille Syrjälä 	/* 45 - 720(1440)x576i@100Hz 16:9 */
976fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
977fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
978a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
9795a11f7f8SClint Taylor 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
9800425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
98178691960SVille Syrjälä 	/* 46 - 1920x1080i@120Hz 16:9 */
982a6b21831SThierry Reding 	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
983a6b21831SThierry Reding 		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
984a6b21831SThierry Reding 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
985ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE),
9860425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
98778691960SVille Syrjälä 	/* 47 - 1280x720@120Hz 16:9 */
988a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
989a6b21831SThierry Reding 		   1430, 1650, 0, 720, 725, 730, 750, 0,
990ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
9910425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
99278691960SVille Syrjälä 	/* 48 - 720x480@120Hz 4:3 */
993a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
994a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
995ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
9960425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
99778691960SVille Syrjälä 	/* 49 - 720x480@120Hz 16:9 */
998a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
999a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
1000ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
10010425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
100278691960SVille Syrjälä 	/* 50 - 720(1440)x480i@120Hz 4:3 */
1003fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
1004fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
1005a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1006ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10070425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
100878691960SVille Syrjälä 	/* 51 - 720(1440)x480i@120Hz 16:9 */
1009fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
1010fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
1011a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1012ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10130425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
101478691960SVille Syrjälä 	/* 52 - 720x576@200Hz 4:3 */
1015a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
1016a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
1017ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
10180425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
101978691960SVille Syrjälä 	/* 53 - 720x576@200Hz 16:9 */
1020a6b21831SThierry Reding 	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
1021a6b21831SThierry Reding 		   796, 864, 0, 576, 581, 586, 625, 0,
1022ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
10230425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
102478691960SVille Syrjälä 	/* 54 - 720(1440)x576i@200Hz 4:3 */
1025fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
1026fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
1027a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1028ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10290425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
103078691960SVille Syrjälä 	/* 55 - 720(1440)x576i@200Hz 16:9 */
1031fb01d280SClint Taylor 	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
1032fb01d280SClint Taylor 		   795, 864, 0, 576, 580, 586, 625, 0,
1033a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1034ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10350425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
103678691960SVille Syrjälä 	/* 56 - 720x480@240Hz 4:3 */
1037a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
1038a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
1039ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
10400425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
104178691960SVille Syrjälä 	/* 57 - 720x480@240Hz 16:9 */
1042a6b21831SThierry Reding 	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
1043a6b21831SThierry Reding 		   798, 858, 0, 480, 489, 495, 525, 0,
1044ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
10450425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
104678691960SVille Syrjälä 	/* 58 - 720(1440)x480i@240Hz 4:3 */
1047fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
1048fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
1049a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1050ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10510425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
105278691960SVille Syrjälä 	/* 59 - 720(1440)x480i@240Hz 16:9 */
1053fb01d280SClint Taylor 	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
1054fb01d280SClint Taylor 		   801, 858, 0, 480, 488, 494, 525, 0,
1055a6b21831SThierry Reding 		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
1056ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
10570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
105878691960SVille Syrjälä 	/* 60 - 1280x720@24Hz 16:9 */
1059a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
1060a6b21831SThierry Reding 		   3080, 3300, 0, 720, 725, 730, 750, 0,
1061ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10620425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
106378691960SVille Syrjälä 	/* 61 - 1280x720@25Hz 16:9 */
1064a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
1065a6b21831SThierry Reding 		   3740, 3960, 0, 720, 725, 730, 750, 0,
1066ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10670425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
106878691960SVille Syrjälä 	/* 62 - 1280x720@30Hz 16:9 */
1069a6b21831SThierry Reding 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
1070a6b21831SThierry Reding 		   3080, 3300, 0, 720, 725, 730, 750, 0,
1071ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10720425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
107378691960SVille Syrjälä 	/* 63 - 1920x1080@120Hz 16:9 */
1074a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
1075a6b21831SThierry Reding 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
1076ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10770425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
107878691960SVille Syrjälä 	/* 64 - 1920x1080@100Hz 16:9 */
1079a6b21831SThierry Reding 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
10808f0e4907SClint Taylor 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
1081ee7925bbSVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10820425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
108378691960SVille Syrjälä 	/* 65 - 1280x720@24Hz 64:27 */
10848ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
10858ec6e075SShashank Sharma 		   3080, 3300, 0, 720, 725, 730, 750, 0,
10868ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10870425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
108878691960SVille Syrjälä 	/* 66 - 1280x720@25Hz 64:27 */
10898ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
10908ec6e075SShashank Sharma 		   3740, 3960, 0, 720, 725, 730, 750, 0,
10918ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10920425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
109378691960SVille Syrjälä 	/* 67 - 1280x720@30Hz 64:27 */
10948ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
10958ec6e075SShashank Sharma 		   3080, 3300, 0, 720, 725, 730, 750, 0,
10968ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
10970425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
109878691960SVille Syrjälä 	/* 68 - 1280x720@50Hz 64:27 */
10998ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
11008ec6e075SShashank Sharma 		   1760, 1980, 0, 720, 725, 730, 750, 0,
11018ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11020425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
110378691960SVille Syrjälä 	/* 69 - 1280x720@60Hz 64:27 */
11048ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
11058ec6e075SShashank Sharma 		   1430, 1650, 0, 720, 725, 730, 750, 0,
11068ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11070425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
110878691960SVille Syrjälä 	/* 70 - 1280x720@100Hz 64:27 */
11098ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
11108ec6e075SShashank Sharma 		   1760, 1980, 0, 720, 725, 730, 750, 0,
11118ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11120425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
111378691960SVille Syrjälä 	/* 71 - 1280x720@120Hz 64:27 */
11148ec6e075SShashank Sharma 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
11158ec6e075SShashank Sharma 		   1430, 1650, 0, 720, 725, 730, 750, 0,
11168ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11170425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
111878691960SVille Syrjälä 	/* 72 - 1920x1080@24Hz 64:27 */
11198ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
11208ec6e075SShashank Sharma 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
11218ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11220425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
112378691960SVille Syrjälä 	/* 73 - 1920x1080@25Hz 64:27 */
11248ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
11258ec6e075SShashank Sharma 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
11268ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11270425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
112878691960SVille Syrjälä 	/* 74 - 1920x1080@30Hz 64:27 */
11298ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
11308ec6e075SShashank Sharma 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
11318ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11320425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
113378691960SVille Syrjälä 	/* 75 - 1920x1080@50Hz 64:27 */
11348ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
11358ec6e075SShashank Sharma 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
11368ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11370425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
113878691960SVille Syrjälä 	/* 76 - 1920x1080@60Hz 64:27 */
11398ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
11408ec6e075SShashank Sharma 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
11418ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11420425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
114378691960SVille Syrjälä 	/* 77 - 1920x1080@100Hz 64:27 */
11448ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
11458ec6e075SShashank Sharma 		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
11468ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11470425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
114878691960SVille Syrjälä 	/* 78 - 1920x1080@120Hz 64:27 */
11498ec6e075SShashank Sharma 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
11508ec6e075SShashank Sharma 		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
11518ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11520425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
115378691960SVille Syrjälä 	/* 79 - 1680x720@24Hz 64:27 */
11548ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
11558ec6e075SShashank Sharma 		   3080, 3300, 0, 720, 725, 730, 750, 0,
11568ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
115878691960SVille Syrjälä 	/* 80 - 1680x720@25Hz 64:27 */
11598ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
11608ec6e075SShashank Sharma 		   2948, 3168, 0, 720, 725, 730, 750, 0,
11618ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11620425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
116378691960SVille Syrjälä 	/* 81 - 1680x720@30Hz 64:27 */
11648ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
11658ec6e075SShashank Sharma 		   2420, 2640, 0, 720, 725, 730, 750, 0,
11668ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11670425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
116878691960SVille Syrjälä 	/* 82 - 1680x720@50Hz 64:27 */
11698ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
11708ec6e075SShashank Sharma 		   1980, 2200, 0, 720, 725, 730, 750, 0,
11718ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11720425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
117378691960SVille Syrjälä 	/* 83 - 1680x720@60Hz 64:27 */
11748ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
11758ec6e075SShashank Sharma 		   1980, 2200, 0, 720, 725, 730, 750, 0,
11768ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11770425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
117878691960SVille Syrjälä 	/* 84 - 1680x720@100Hz 64:27 */
11798ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
11808ec6e075SShashank Sharma 		   1780, 2000, 0, 720, 725, 730, 825, 0,
11818ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11820425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
118378691960SVille Syrjälä 	/* 85 - 1680x720@120Hz 64:27 */
11848ec6e075SShashank Sharma 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
11858ec6e075SShashank Sharma 		   1780, 2000, 0, 720, 725, 730, 825, 0,
11868ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11870425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
118878691960SVille Syrjälä 	/* 86 - 2560x1080@24Hz 64:27 */
11898ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
11908ec6e075SShashank Sharma 		   3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
11918ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11920425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
119378691960SVille Syrjälä 	/* 87 - 2560x1080@25Hz 64:27 */
11948ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
11958ec6e075SShashank Sharma 		   3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
11968ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
11970425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
119878691960SVille Syrjälä 	/* 88 - 2560x1080@30Hz 64:27 */
11998ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
12008ec6e075SShashank Sharma 		   3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
12018ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12020425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
120378691960SVille Syrjälä 	/* 89 - 2560x1080@50Hz 64:27 */
12048ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
12058ec6e075SShashank Sharma 		   3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
12068ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12070425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
120878691960SVille Syrjälä 	/* 90 - 2560x1080@60Hz 64:27 */
12098ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
12108ec6e075SShashank Sharma 		   2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
12118ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12120425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
121378691960SVille Syrjälä 	/* 91 - 2560x1080@100Hz 64:27 */
12148ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
12158ec6e075SShashank Sharma 		   2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
12168ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12170425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
121878691960SVille Syrjälä 	/* 92 - 2560x1080@120Hz 64:27 */
12198ec6e075SShashank Sharma 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
12208ec6e075SShashank Sharma 		   3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
12218ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12220425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
122378691960SVille Syrjälä 	/* 93 - 3840x2160@24Hz 16:9 */
12248ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
12258ec6e075SShashank Sharma 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
12268ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12270425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
122878691960SVille Syrjälä 	/* 94 - 3840x2160@25Hz 16:9 */
12298ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
12308ec6e075SShashank Sharma 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
12318ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12320425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
123378691960SVille Syrjälä 	/* 95 - 3840x2160@30Hz 16:9 */
12348ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
12358ec6e075SShashank Sharma 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
12368ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12370425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
123878691960SVille Syrjälä 	/* 96 - 3840x2160@50Hz 16:9 */
12398ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
12408ec6e075SShashank Sharma 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
12418ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12420425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
124378691960SVille Syrjälä 	/* 97 - 3840x2160@60Hz 16:9 */
12448ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
12458ec6e075SShashank Sharma 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
12468ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12470425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
124878691960SVille Syrjälä 	/* 98 - 4096x2160@24Hz 256:135 */
12498ec6e075SShashank Sharma 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
12508ec6e075SShashank Sharma 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
12518ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12520425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
125378691960SVille Syrjälä 	/* 99 - 4096x2160@25Hz 256:135 */
12548ec6e075SShashank Sharma 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
12558ec6e075SShashank Sharma 		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
12568ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
125878691960SVille Syrjälä 	/* 100 - 4096x2160@30Hz 256:135 */
12598ec6e075SShashank Sharma 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
12608ec6e075SShashank Sharma 		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
12618ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12620425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
126378691960SVille Syrjälä 	/* 101 - 4096x2160@50Hz 256:135 */
12648ec6e075SShashank Sharma 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
12658ec6e075SShashank Sharma 		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
12668ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12670425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
126878691960SVille Syrjälä 	/* 102 - 4096x2160@60Hz 256:135 */
12698ec6e075SShashank Sharma 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
12708ec6e075SShashank Sharma 		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
12718ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12720425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
127378691960SVille Syrjälä 	/* 103 - 3840x2160@24Hz 64:27 */
12748ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
12758ec6e075SShashank Sharma 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
12768ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12770425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
127878691960SVille Syrjälä 	/* 104 - 3840x2160@25Hz 64:27 */
12798ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
12808ec6e075SShashank Sharma 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
12818ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12820425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
128378691960SVille Syrjälä 	/* 105 - 3840x2160@30Hz 64:27 */
12848ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
12858ec6e075SShashank Sharma 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
12868ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12870425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
128878691960SVille Syrjälä 	/* 106 - 3840x2160@50Hz 64:27 */
12898ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
12908ec6e075SShashank Sharma 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
12918ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12920425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
129378691960SVille Syrjälä 	/* 107 - 3840x2160@60Hz 64:27 */
12948ec6e075SShashank Sharma 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
12958ec6e075SShashank Sharma 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
12968ec6e075SShashank Sharma 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
12970425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1298978f6b06SVille Syrjälä 	/* 108 - 1280x720@48Hz 16:9 */
1299978f6b06SVille Syrjälä 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
1300978f6b06SVille Syrjälä 		   2280, 2500, 0, 720, 725, 730, 750, 0,
1301978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13020425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1303978f6b06SVille Syrjälä 	/* 109 - 1280x720@48Hz 64:27 */
1304978f6b06SVille Syrjälä 	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
1305978f6b06SVille Syrjälä 		   2280, 2500, 0, 720, 725, 730, 750, 0,
1306978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13070425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1308978f6b06SVille Syrjälä 	/* 110 - 1680x720@48Hz 64:27 */
1309978f6b06SVille Syrjälä 	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 2490,
1310978f6b06SVille Syrjälä 		   2530, 2750, 0, 720, 725, 730, 750, 0,
1311978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13120425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1313978f6b06SVille Syrjälä 	/* 111 - 1920x1080@48Hz 16:9 */
1314978f6b06SVille Syrjälä 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
1315978f6b06SVille Syrjälä 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
1316978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13170425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1318978f6b06SVille Syrjälä 	/* 112 - 1920x1080@48Hz 64:27 */
1319978f6b06SVille Syrjälä 	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
1320978f6b06SVille Syrjälä 		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
1321978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13220425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1323978f6b06SVille Syrjälä 	/* 113 - 2560x1080@48Hz 64:27 */
1324978f6b06SVille Syrjälä 	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 3558,
1325978f6b06SVille Syrjälä 		   3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
1326978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13270425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1328978f6b06SVille Syrjälä 	/* 114 - 3840x2160@48Hz 16:9 */
1329978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
1330978f6b06SVille Syrjälä 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1331978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13320425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1333978f6b06SVille Syrjälä 	/* 115 - 4096x2160@48Hz 256:135 */
1334978f6b06SVille Syrjälä 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5116,
1335978f6b06SVille Syrjälä 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1336978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13370425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1338978f6b06SVille Syrjälä 	/* 116 - 3840x2160@48Hz 64:27 */
1339978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
1340978f6b06SVille Syrjälä 		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
1341978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13420425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1343978f6b06SVille Syrjälä 	/* 117 - 3840x2160@100Hz 16:9 */
1344978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
1345978f6b06SVille Syrjälä 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1346978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13470425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1348978f6b06SVille Syrjälä 	/* 118 - 3840x2160@120Hz 16:9 */
1349978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
1350978f6b06SVille Syrjälä 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1351978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13520425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1353978f6b06SVille Syrjälä 	/* 119 - 3840x2160@100Hz 64:27 */
1354978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
1355978f6b06SVille Syrjälä 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1356978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13570425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1358978f6b06SVille Syrjälä 	/* 120 - 3840x2160@120Hz 64:27 */
1359978f6b06SVille Syrjälä 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
1360978f6b06SVille Syrjälä 		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
1361978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13620425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1363978f6b06SVille Syrjälä 	/* 121 - 5120x2160@24Hz 64:27 */
1364978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 7116,
1365978f6b06SVille Syrjälä 		   7204, 7500, 0, 2160, 2168, 2178, 2200, 0,
1366978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13670425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1368978f6b06SVille Syrjälä 	/* 122 - 5120x2160@25Hz 64:27 */
1369978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 6816,
1370978f6b06SVille Syrjälä 		   6904, 7200, 0, 2160, 2168, 2178, 2200, 0,
1371978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13720425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1373978f6b06SVille Syrjälä 	/* 123 - 5120x2160@30Hz 64:27 */
1374978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 5784,
1375978f6b06SVille Syrjälä 		   5872, 6000, 0, 2160, 2168, 2178, 2200, 0,
1376978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13770425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1378978f6b06SVille Syrjälä 	/* 124 - 5120x2160@48Hz 64:27 */
1379978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5866,
1380978f6b06SVille Syrjälä 		   5954, 6250, 0, 2160, 2168, 2178, 2475, 0,
1381978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13820425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1383978f6b06SVille Syrjälä 	/* 125 - 5120x2160@50Hz 64:27 */
1384978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 6216,
1385978f6b06SVille Syrjälä 		   6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
1386978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13870425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1388978f6b06SVille Syrjälä 	/* 126 - 5120x2160@60Hz 64:27 */
1389978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5284,
1390978f6b06SVille Syrjälä 		   5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
1391978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13920425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1393978f6b06SVille Syrjälä 	/* 127 - 5120x2160@100Hz 64:27 */
1394978f6b06SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 6216,
1395978f6b06SVille Syrjälä 		   6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
1396978f6b06SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
13970425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1398a6b21831SThierry Reding };
1399a6b21831SThierry Reding 
14007ebe1963SLespiau, Damien /*
1401f7655d42SVille Syrjälä  * From CEA/CTA-861 spec.
1402f7655d42SVille Syrjälä  *
1403f7655d42SVille Syrjälä  * Do not access directly, instead always use cea_mode_for_vic().
1404f7655d42SVille Syrjälä  */
1405f7655d42SVille Syrjälä static const struct drm_display_mode edid_cea_modes_193[] = {
1406f7655d42SVille Syrjälä 	/* 193 - 5120x2160@120Hz 64:27 */
1407f7655d42SVille Syrjälä 	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 5284,
1408f7655d42SVille Syrjälä 		   5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
1409f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14100425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1411f7655d42SVille Syrjälä 	/* 194 - 7680x4320@24Hz 16:9 */
1412f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10232,
1413f7655d42SVille Syrjälä 		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
1414f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14150425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1416f7655d42SVille Syrjälä 	/* 195 - 7680x4320@25Hz 16:9 */
1417f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10032,
1418f7655d42SVille Syrjälä 		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
1419f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14200425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1421f7655d42SVille Syrjälä 	/* 196 - 7680x4320@30Hz 16:9 */
1422f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 8232,
1423f7655d42SVille Syrjälä 		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
1424f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14250425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1426f7655d42SVille Syrjälä 	/* 197 - 7680x4320@48Hz 16:9 */
1427f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10232,
1428f7655d42SVille Syrjälä 		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
1429f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14300425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1431f7655d42SVille Syrjälä 	/* 198 - 7680x4320@50Hz 16:9 */
1432f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10032,
1433f7655d42SVille Syrjälä 		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
1434f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14350425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1436f7655d42SVille Syrjälä 	/* 199 - 7680x4320@60Hz 16:9 */
1437f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 8232,
1438f7655d42SVille Syrjälä 		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
1439f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14400425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1441f7655d42SVille Syrjälä 	/* 200 - 7680x4320@100Hz 16:9 */
1442f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 9792,
1443f7655d42SVille Syrjälä 		   9968, 10560, 0, 4320, 4336, 4356, 4500, 0,
1444f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14450425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1446f7655d42SVille Syrjälä 	/* 201 - 7680x4320@120Hz 16:9 */
1447f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 8032,
1448f7655d42SVille Syrjälä 		   8208, 8800, 0, 4320, 4336, 4356, 4500, 0,
1449f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14500425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
1451f7655d42SVille Syrjälä 	/* 202 - 7680x4320@24Hz 64:27 */
1452f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10232,
1453f7655d42SVille Syrjälä 		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
1454f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14550425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1456f7655d42SVille Syrjälä 	/* 203 - 7680x4320@25Hz 64:27 */
1457f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10032,
1458f7655d42SVille Syrjälä 		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
1459f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14600425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1461f7655d42SVille Syrjälä 	/* 204 - 7680x4320@30Hz 64:27 */
1462f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 8232,
1463f7655d42SVille Syrjälä 		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
1464f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14650425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1466f7655d42SVille Syrjälä 	/* 205 - 7680x4320@48Hz 64:27 */
1467f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10232,
1468f7655d42SVille Syrjälä 		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
1469f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14700425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1471f7655d42SVille Syrjälä 	/* 206 - 7680x4320@50Hz 64:27 */
1472f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10032,
1473f7655d42SVille Syrjälä 		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
1474f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14750425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1476f7655d42SVille Syrjälä 	/* 207 - 7680x4320@60Hz 64:27 */
1477f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 8232,
1478f7655d42SVille Syrjälä 		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
1479f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14800425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1481f7655d42SVille Syrjälä 	/* 208 - 7680x4320@100Hz 64:27 */
1482f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 9792,
1483f7655d42SVille Syrjälä 		   9968, 10560, 0, 4320, 4336, 4356, 4500, 0,
1484f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14850425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1486f7655d42SVille Syrjälä 	/* 209 - 7680x4320@120Hz 64:27 */
1487f7655d42SVille Syrjälä 	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 8032,
1488f7655d42SVille Syrjälä 		   8208, 8800, 0, 4320, 4336, 4356, 4500, 0,
1489f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14900425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1491f7655d42SVille Syrjälä 	/* 210 - 10240x4320@24Hz 64:27 */
1492f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 11732,
1493f7655d42SVille Syrjälä 		   11908, 12500, 0, 4320, 4336, 4356, 4950, 0,
1494f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
14950425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1496f7655d42SVille Syrjälä 	/* 211 - 10240x4320@25Hz 64:27 */
1497f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 12732,
1498f7655d42SVille Syrjälä 		   12908, 13500, 0, 4320, 4336, 4356, 4400, 0,
1499f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15000425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1501f7655d42SVille Syrjälä 	/* 212 - 10240x4320@30Hz 64:27 */
1502f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 10528,
1503f7655d42SVille Syrjälä 		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
1504f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15050425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1506f7655d42SVille Syrjälä 	/* 213 - 10240x4320@48Hz 64:27 */
1507f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 11732,
1508f7655d42SVille Syrjälä 		   11908, 12500, 0, 4320, 4336, 4356, 4950, 0,
1509f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15100425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1511f7655d42SVille Syrjälä 	/* 214 - 10240x4320@50Hz 64:27 */
1512f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 12732,
1513f7655d42SVille Syrjälä 		   12908, 13500, 0, 4320, 4336, 4356, 4400, 0,
1514f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15150425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1516f7655d42SVille Syrjälä 	/* 215 - 10240x4320@60Hz 64:27 */
1517f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 10528,
1518f7655d42SVille Syrjälä 		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
1519f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15200425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1521f7655d42SVille Syrjälä 	/* 216 - 10240x4320@100Hz 64:27 */
1522f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 5940000, 10240, 12432,
1523f7655d42SVille Syrjälä 		   12608, 13200, 0, 4320, 4336, 4356, 4500, 0,
1524f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15250425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1526f7655d42SVille Syrjälä 	/* 217 - 10240x4320@120Hz 64:27 */
1527f7655d42SVille Syrjälä 	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 5940000, 10240, 10528,
1528f7655d42SVille Syrjälä 		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
1529f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15300425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
1531f7655d42SVille Syrjälä 	/* 218 - 4096x2160@100Hz 256:135 */
1532f7655d42SVille Syrjälä 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 1188000, 4096, 4896,
1533f7655d42SVille Syrjälä 		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
1534f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15350425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1536f7655d42SVille Syrjälä 	/* 219 - 4096x2160@120Hz 256:135 */
1537f7655d42SVille Syrjälä 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 1188000, 4096, 4184,
1538f7655d42SVille Syrjälä 		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
1539f7655d42SVille Syrjälä 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15400425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
1541f7655d42SVille Syrjälä };
1542f7655d42SVille Syrjälä 
1543f7655d42SVille Syrjälä /*
1544d9278b4cSJani Nikula  * HDMI 1.4 4k modes. Index using the VIC.
15457ebe1963SLespiau, Damien  */
15467ebe1963SLespiau, Damien static const struct drm_display_mode edid_4k_modes[] = {
1547d9278b4cSJani Nikula 	/* 0 - dummy, VICs start at 1 */
1548d9278b4cSJani Nikula 	{ },
15497ebe1963SLespiau, Damien 	/* 1 - 3840x2160@30Hz */
15507ebe1963SLespiau, Damien 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
15517ebe1963SLespiau, Damien 		   3840, 4016, 4104, 4400, 0,
15527ebe1963SLespiau, Damien 		   2160, 2168, 2178, 2250, 0,
15537ebe1963SLespiau, Damien 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15540425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
15557ebe1963SLespiau, Damien 	/* 2 - 3840x2160@25Hz */
15567ebe1963SLespiau, Damien 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
15577ebe1963SLespiau, Damien 		   3840, 4896, 4984, 5280, 0,
15587ebe1963SLespiau, Damien 		   2160, 2168, 2178, 2250, 0,
15597ebe1963SLespiau, Damien 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15600425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
15617ebe1963SLespiau, Damien 	/* 3 - 3840x2160@24Hz */
15627ebe1963SLespiau, Damien 	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
15637ebe1963SLespiau, Damien 		   3840, 5116, 5204, 5500, 0,
15647ebe1963SLespiau, Damien 		   2160, 2168, 2178, 2250, 0,
15657ebe1963SLespiau, Damien 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15660425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
15677ebe1963SLespiau, Damien 	/* 4 - 4096x2160@24Hz (SMPTE) */
15687ebe1963SLespiau, Damien 	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000,
15697ebe1963SLespiau, Damien 		   4096, 5116, 5204, 5500, 0,
15707ebe1963SLespiau, Damien 		   2160, 2168, 2178, 2250, 0,
15717ebe1963SLespiau, Damien 		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
15720425662fSVille Syrjälä 	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
15737ebe1963SLespiau, Damien };
15747ebe1963SLespiau, Damien 
157561e57a8dSAdam Jackson /*** DDC fetch and block validation ***/
1576f453ba04SDave Airlie 
1577e4ccf9a7SJani Nikula /*
1578e4ccf9a7SJani Nikula  * The opaque EDID type, internal to drm_edid.c.
1579e4ccf9a7SJani Nikula  */
1580e4ccf9a7SJani Nikula struct drm_edid {
1581e4ccf9a7SJani Nikula 	/* Size allocated for edid */
1582e4ccf9a7SJani Nikula 	size_t size;
1583e4ccf9a7SJani Nikula 	const struct edid *edid;
1584e4ccf9a7SJani Nikula };
1585e4ccf9a7SJani Nikula 
158618e3c1d5SJani Nikula static int edid_hfeeodb_extension_block_count(const struct edid *edid);
158718e3c1d5SJani Nikula 
edid_hfeeodb_block_count(const struct edid * edid)158818e3c1d5SJani Nikula static int edid_hfeeodb_block_count(const struct edid *edid)
158918e3c1d5SJani Nikula {
159018e3c1d5SJani Nikula 	int eeodb = edid_hfeeodb_extension_block_count(edid);
159118e3c1d5SJani Nikula 
159218e3c1d5SJani Nikula 	return eeodb ? eeodb + 1 : 0;
159318e3c1d5SJani Nikula }
159418e3c1d5SJani Nikula 
edid_extension_block_count(const struct edid * edid)1595f1e4c916SJani Nikula static int edid_extension_block_count(const struct edid *edid)
1596f1e4c916SJani Nikula {
1597f1e4c916SJani Nikula 	return edid->extensions;
1598f1e4c916SJani Nikula }
1599f1e4c916SJani Nikula 
edid_block_count(const struct edid * edid)1600f1e4c916SJani Nikula static int edid_block_count(const struct edid *edid)
1601f1e4c916SJani Nikula {
1602f1e4c916SJani Nikula 	return edid_extension_block_count(edid) + 1;
1603f1e4c916SJani Nikula }
1604f1e4c916SJani Nikula 
edid_size_by_blocks(int num_blocks)1605f1e4c916SJani Nikula static int edid_size_by_blocks(int num_blocks)
1606f1e4c916SJani Nikula {
1607f1e4c916SJani Nikula 	return num_blocks * EDID_LENGTH;
1608f1e4c916SJani Nikula }
1609f1e4c916SJani Nikula 
edid_size(const struct edid * edid)1610f1e4c916SJani Nikula static int edid_size(const struct edid *edid)
1611f1e4c916SJani Nikula {
1612f1e4c916SJani Nikula 	return edid_size_by_blocks(edid_block_count(edid));
1613f1e4c916SJani Nikula }
1614f1e4c916SJani Nikula 
edid_block_data(const struct edid * edid,int index)1615f1e4c916SJani Nikula static const void *edid_block_data(const struct edid *edid, int index)
1616f1e4c916SJani Nikula {
1617f1e4c916SJani Nikula 	BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH);
1618f1e4c916SJani Nikula 
1619f1e4c916SJani Nikula 	return edid + index;
1620f1e4c916SJani Nikula }
1621f1e4c916SJani Nikula 
edid_extension_block_data(const struct edid * edid,int index)1622f1e4c916SJani Nikula static const void *edid_extension_block_data(const struct edid *edid, int index)
1623f1e4c916SJani Nikula {
1624f1e4c916SJani Nikula 	return edid_block_data(edid, index + 1);
1625f1e4c916SJani Nikula }
1626f1e4c916SJani Nikula 
1627b16c9e6cSJani Nikula /* EDID block count indicated in EDID, may exceed allocated size */
__drm_edid_block_count(const struct drm_edid * drm_edid)1628b16c9e6cSJani Nikula static int __drm_edid_block_count(const struct drm_edid *drm_edid)
1629d9307f27SJani Nikula {
1630d9307f27SJani Nikula 	int num_blocks;
1631d9307f27SJani Nikula 
1632d9307f27SJani Nikula 	/* Starting point */
1633d9307f27SJani Nikula 	num_blocks = edid_block_count(drm_edid->edid);
1634d9307f27SJani Nikula 
1635b1dee952SJani Nikula 	/* HF-EEODB override */
1636b1dee952SJani Nikula 	if (drm_edid->size >= edid_size_by_blocks(2)) {
1637b1dee952SJani Nikula 		int eeodb;
1638b1dee952SJani Nikula 
1639b1dee952SJani Nikula 		/*
1640b1dee952SJani Nikula 		 * Note: HF-EEODB may specify a smaller extension count than the
1641b1dee952SJani Nikula 		 * regular one. Unlike in buffer allocation, here we can use it.
1642b1dee952SJani Nikula 		 */
1643b1dee952SJani Nikula 		eeodb = edid_hfeeodb_block_count(drm_edid->edid);
1644b1dee952SJani Nikula 		if (eeodb)
1645b1dee952SJani Nikula 			num_blocks = eeodb;
1646b1dee952SJani Nikula 	}
1647b1dee952SJani Nikula 
1648d9307f27SJani Nikula 	return num_blocks;
1649d9307f27SJani Nikula }
1650d9307f27SJani Nikula 
1651b16c9e6cSJani Nikula /* EDID block count, limited by allocated size */
drm_edid_block_count(const struct drm_edid * drm_edid)1652b16c9e6cSJani Nikula static int drm_edid_block_count(const struct drm_edid *drm_edid)
1653b16c9e6cSJani Nikula {
1654b16c9e6cSJani Nikula 	/* Limit by allocated size */
1655b16c9e6cSJani Nikula 	return min(__drm_edid_block_count(drm_edid),
1656b16c9e6cSJani Nikula 		   (int)drm_edid->size / EDID_LENGTH);
1657b16c9e6cSJani Nikula }
1658b16c9e6cSJani Nikula 
1659b16c9e6cSJani Nikula /* EDID extension block count, limited by allocated size */
drm_edid_extension_block_count(const struct drm_edid * drm_edid)1660d9307f27SJani Nikula static int drm_edid_extension_block_count(const struct drm_edid *drm_edid)
1661d9307f27SJani Nikula {
1662d9307f27SJani Nikula 	return drm_edid_block_count(drm_edid) - 1;
1663d9307f27SJani Nikula }
1664d9307f27SJani Nikula 
drm_edid_block_data(const struct drm_edid * drm_edid,int index)1665d9307f27SJani Nikula static const void *drm_edid_block_data(const struct drm_edid *drm_edid, int index)
1666d9307f27SJani Nikula {
1667d9307f27SJani Nikula 	return edid_block_data(drm_edid->edid, index);
1668d9307f27SJani Nikula }
1669d9307f27SJani Nikula 
drm_edid_extension_block_data(const struct drm_edid * drm_edid,int index)1670d9307f27SJani Nikula static const void *drm_edid_extension_block_data(const struct drm_edid *drm_edid,
1671d9307f27SJani Nikula 						 int index)
1672d9307f27SJani Nikula {
1673d9307f27SJani Nikula 	return edid_extension_block_data(drm_edid->edid, index);
1674d9307f27SJani Nikula }
1675d9307f27SJani Nikula 
167694afc538SJani Nikula /*
167722a27e05SJani Nikula  * Initializer helper for legacy interfaces, where we have no choice but to
167822a27e05SJani Nikula  * trust edid size. Not for general purpose use.
167922a27e05SJani Nikula  */
drm_edid_legacy_init(struct drm_edid * drm_edid,const struct edid * edid)168022a27e05SJani Nikula static const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid,
168122a27e05SJani Nikula 						   const struct edid *edid)
168222a27e05SJani Nikula {
168322a27e05SJani Nikula 	if (!edid)
168422a27e05SJani Nikula 		return NULL;
168522a27e05SJani Nikula 
168622a27e05SJani Nikula 	memset(drm_edid, 0, sizeof(*drm_edid));
168722a27e05SJani Nikula 
168822a27e05SJani Nikula 	drm_edid->edid = edid;
168922a27e05SJani Nikula 	drm_edid->size = edid_size(edid);
169022a27e05SJani Nikula 
169122a27e05SJani Nikula 	return drm_edid;
169222a27e05SJani Nikula }
169322a27e05SJani Nikula 
169422a27e05SJani Nikula /*
169594afc538SJani Nikula  * EDID base and extension block iterator.
169694afc538SJani Nikula  *
169794afc538SJani Nikula  * struct drm_edid_iter iter;
169894afc538SJani Nikula  * const u8 *block;
169994afc538SJani Nikula  *
1700bbded689SJani Nikula  * drm_edid_iter_begin(drm_edid, &iter);
170194afc538SJani Nikula  * drm_edid_iter_for_each(block, &iter) {
170294afc538SJani Nikula  *         // do stuff with block
170394afc538SJani Nikula  * }
170494afc538SJani Nikula  * drm_edid_iter_end(&iter);
170594afc538SJani Nikula  */
170694afc538SJani Nikula struct drm_edid_iter {
1707bbded689SJani Nikula 	const struct drm_edid *drm_edid;
170894afc538SJani Nikula 
170994afc538SJani Nikula 	/* Current block index. */
171094afc538SJani Nikula 	int index;
171194afc538SJani Nikula };
171294afc538SJani Nikula 
drm_edid_iter_begin(const struct drm_edid * drm_edid,struct drm_edid_iter * iter)1713bbded689SJani Nikula static void drm_edid_iter_begin(const struct drm_edid *drm_edid,
171494afc538SJani Nikula 				struct drm_edid_iter *iter)
171594afc538SJani Nikula {
171694afc538SJani Nikula 	memset(iter, 0, sizeof(*iter));
171794afc538SJani Nikula 
1718bbded689SJani Nikula 	iter->drm_edid = drm_edid;
171994afc538SJani Nikula }
172094afc538SJani Nikula 
__drm_edid_iter_next(struct drm_edid_iter * iter)172194afc538SJani Nikula static const void *__drm_edid_iter_next(struct drm_edid_iter *iter)
172294afc538SJani Nikula {
172394afc538SJani Nikula 	const void *block = NULL;
172494afc538SJani Nikula 
1725bbded689SJani Nikula 	if (!iter->drm_edid)
172694afc538SJani Nikula 		return NULL;
172794afc538SJani Nikula 
1728d9307f27SJani Nikula 	if (iter->index < drm_edid_block_count(iter->drm_edid))
1729d9307f27SJani Nikula 		block = drm_edid_block_data(iter->drm_edid, iter->index++);
173094afc538SJani Nikula 
173194afc538SJani Nikula 	return block;
173294afc538SJani Nikula }
173394afc538SJani Nikula 
173494afc538SJani Nikula #define drm_edid_iter_for_each(__block, __iter)			\
173594afc538SJani Nikula 	while (((__block) = __drm_edid_iter_next(__iter)))
173694afc538SJani Nikula 
drm_edid_iter_end(struct drm_edid_iter * iter)173794afc538SJani Nikula static void drm_edid_iter_end(struct drm_edid_iter *iter)
173894afc538SJani Nikula {
173994afc538SJani Nikula 	memset(iter, 0, sizeof(*iter));
174094afc538SJani Nikula }
174194afc538SJani Nikula 
1742083ae056SAdam Jackson static const u8 edid_header[] = {
1743083ae056SAdam Jackson 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
1744083ae056SAdam Jackson };
1745f453ba04SDave Airlie 
edid_header_fix(void * edid)17460a612bbdSJani Nikula static void edid_header_fix(void *edid)
17470a612bbdSJani Nikula {
17480a612bbdSJani Nikula 	memcpy(edid, edid_header, sizeof(edid_header));
17490a612bbdSJani Nikula }
17500a612bbdSJani Nikula 
1751db6cf833SThierry Reding /**
1752db6cf833SThierry Reding  * drm_edid_header_is_valid - sanity check the header of the base EDID block
17535d96fc9cSJani Nikula  * @_edid: pointer to raw base EDID block
1754db6cf833SThierry Reding  *
1755db6cf833SThierry Reding  * Sanity check the header of the base EDID block.
1756db6cf833SThierry Reding  *
1757db6cf833SThierry Reding  * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
1758051963d4SThomas Reim  */
drm_edid_header_is_valid(const void * _edid)17596d987dddSJani Nikula int drm_edid_header_is_valid(const void *_edid)
1760051963d4SThomas Reim {
17616d987dddSJani Nikula 	const struct edid *edid = _edid;
1762051963d4SThomas Reim 	int i, score = 0;
1763051963d4SThomas Reim 
17646d987dddSJani Nikula 	for (i = 0; i < sizeof(edid_header); i++) {
17656d987dddSJani Nikula 		if (edid->header[i] == edid_header[i])
1766051963d4SThomas Reim 			score++;
17676d987dddSJani Nikula 	}
1768051963d4SThomas Reim 
1769051963d4SThomas Reim 	return score;
1770051963d4SThomas Reim }
1771051963d4SThomas Reim EXPORT_SYMBOL(drm_edid_header_is_valid);
1772051963d4SThomas Reim 
177347819ba2SAdam Jackson static int edid_fixup __read_mostly = 6;
177447819ba2SAdam Jackson module_param_named(edid_fixup, edid_fixup, int, 0400);
177547819ba2SAdam Jackson MODULE_PARM_DESC(edid_fixup,
177647819ba2SAdam Jackson 		 "Minimum number of valid EDID header bytes (0-8, default 6)");
1777051963d4SThomas Reim 
edid_block_compute_checksum(const void * _block)177870e49ebeSJani Nikula static int edid_block_compute_checksum(const void *_block)
1779c465bbc8SStefan Brüns {
178070e49ebeSJani Nikula 	const u8 *block = _block;
1781c465bbc8SStefan Brüns 	int i;
1782e11f5bd8SJerry (Fangzhi) Zuo 	u8 csum = 0, crc = 0;
1783e11f5bd8SJerry (Fangzhi) Zuo 
1784e11f5bd8SJerry (Fangzhi) Zuo 	for (i = 0; i < EDID_LENGTH - 1; i++)
178570e49ebeSJani Nikula 		csum += block[i];
1786c465bbc8SStefan Brüns 
1787e11f5bd8SJerry (Fangzhi) Zuo 	crc = 0x100 - csum;
1788e11f5bd8SJerry (Fangzhi) Zuo 
1789e11f5bd8SJerry (Fangzhi) Zuo 	return crc;
1790e11f5bd8SJerry (Fangzhi) Zuo }
1791e11f5bd8SJerry (Fangzhi) Zuo 
edid_block_get_checksum(const void * _block)179270e49ebeSJani Nikula static int edid_block_get_checksum(const void *_block)
1793e11f5bd8SJerry (Fangzhi) Zuo {
179470e49ebeSJani Nikula 	const struct edid *block = _block;
179570e49ebeSJani Nikula 
179670e49ebeSJani Nikula 	return block->checksum;
1797c465bbc8SStefan Brüns }
1798c465bbc8SStefan Brüns 
edid_block_tag(const void * _block)17994ba0f53cSJani Nikula static int edid_block_tag(const void *_block)
18004ba0f53cSJani Nikula {
18014ba0f53cSJani Nikula 	const u8 *block = _block;
18024ba0f53cSJani Nikula 
18034ba0f53cSJani Nikula 	return block[0];
18044ba0f53cSJani Nikula }
18054ba0f53cSJani Nikula 
edid_block_is_zero(const void * edid)18068baccb27SJani Nikula static bool edid_block_is_zero(const void *edid)
1807d6885d65SStefan Brüns {
18088baccb27SJani Nikula 	return !memchr_inv(edid, 0, EDID_LENGTH);
1809d6885d65SStefan Brüns }
1810d6885d65SStefan Brüns 
1811db6cf833SThierry Reding /**
1812536faa45SStanislav Lisovskiy  * drm_edid_are_equal - compare two edid blobs.
1813536faa45SStanislav Lisovskiy  * @edid1: pointer to first blob
1814536faa45SStanislav Lisovskiy  * @edid2: pointer to second blob
1815536faa45SStanislav Lisovskiy  * This helper can be used during probing to determine if
1816536faa45SStanislav Lisovskiy  * edid had changed.
1817536faa45SStanislav Lisovskiy  */
drm_edid_are_equal(const struct edid * edid1,const struct edid * edid2)1818536faa45SStanislav Lisovskiy bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
1819536faa45SStanislav Lisovskiy {
1820536faa45SStanislav Lisovskiy 	int edid1_len, edid2_len;
1821536faa45SStanislav Lisovskiy 	bool edid1_present = edid1 != NULL;
1822536faa45SStanislav Lisovskiy 	bool edid2_present = edid2 != NULL;
1823536faa45SStanislav Lisovskiy 
1824536faa45SStanislav Lisovskiy 	if (edid1_present != edid2_present)
1825536faa45SStanislav Lisovskiy 		return false;
1826536faa45SStanislav Lisovskiy 
1827536faa45SStanislav Lisovskiy 	if (edid1) {
1828f1e4c916SJani Nikula 		edid1_len = edid_size(edid1);
1829f1e4c916SJani Nikula 		edid2_len = edid_size(edid2);
1830536faa45SStanislav Lisovskiy 
1831536faa45SStanislav Lisovskiy 		if (edid1_len != edid2_len)
1832536faa45SStanislav Lisovskiy 			return false;
1833536faa45SStanislav Lisovskiy 
1834536faa45SStanislav Lisovskiy 		if (memcmp(edid1, edid2, edid1_len))
1835536faa45SStanislav Lisovskiy 			return false;
1836536faa45SStanislav Lisovskiy 	}
1837536faa45SStanislav Lisovskiy 
1838536faa45SStanislav Lisovskiy 	return true;
1839536faa45SStanislav Lisovskiy }
1840536faa45SStanislav Lisovskiy EXPORT_SYMBOL(drm_edid_are_equal);
1841536faa45SStanislav Lisovskiy 
18421f221284SJani Nikula enum edid_block_status {
18431f221284SJani Nikula 	EDID_BLOCK_OK = 0,
18442deaf1c2SJani Nikula 	EDID_BLOCK_READ_FAIL,
18451f221284SJani Nikula 	EDID_BLOCK_NULL,
184649dc0558SJani Nikula 	EDID_BLOCK_ZERO,
18471f221284SJani Nikula 	EDID_BLOCK_HEADER_CORRUPT,
18481f221284SJani Nikula 	EDID_BLOCK_HEADER_REPAIR,
18491f221284SJani Nikula 	EDID_BLOCK_HEADER_FIXED,
18501f221284SJani Nikula 	EDID_BLOCK_CHECKSUM,
18511f221284SJani Nikula 	EDID_BLOCK_VERSION,
18521f221284SJani Nikula };
18531f221284SJani Nikula 
edid_block_check(const void * _block,bool is_base_block)18541f221284SJani Nikula static enum edid_block_status edid_block_check(const void *_block,
18551f221284SJani Nikula 					       bool is_base_block)
18561f221284SJani Nikula {
18571f221284SJani Nikula 	const struct edid *block = _block;
18581f221284SJani Nikula 
18591f221284SJani Nikula 	if (!block)
18601f221284SJani Nikula 		return EDID_BLOCK_NULL;
18611f221284SJani Nikula 
18621f221284SJani Nikula 	if (is_base_block) {
18631f221284SJani Nikula 		int score = drm_edid_header_is_valid(block);
18641f221284SJani Nikula 
186549dc0558SJani Nikula 		if (score < clamp(edid_fixup, 0, 8)) {
186649dc0558SJani Nikula 			if (edid_block_is_zero(block))
186749dc0558SJani Nikula 				return EDID_BLOCK_ZERO;
186849dc0558SJani Nikula 			else
18691f221284SJani Nikula 				return EDID_BLOCK_HEADER_CORRUPT;
187049dc0558SJani Nikula 		}
18711f221284SJani Nikula 
18721f221284SJani Nikula 		if (score < 8)
18731f221284SJani Nikula 			return EDID_BLOCK_HEADER_REPAIR;
18741f221284SJani Nikula 	}
18751f221284SJani Nikula 
187649dc0558SJani Nikula 	if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) {
187749dc0558SJani Nikula 		if (edid_block_is_zero(block))
187849dc0558SJani Nikula 			return EDID_BLOCK_ZERO;
187949dc0558SJani Nikula 		else
18801f221284SJani Nikula 			return EDID_BLOCK_CHECKSUM;
188149dc0558SJani Nikula 	}
18821f221284SJani Nikula 
18831f221284SJani Nikula 	if (is_base_block) {
18841f221284SJani Nikula 		if (block->version != 1)
18851f221284SJani Nikula 			return EDID_BLOCK_VERSION;
18861f221284SJani Nikula 	}
18871f221284SJani Nikula 
18881f221284SJani Nikula 	return EDID_BLOCK_OK;
18891f221284SJani Nikula }
18901f221284SJani Nikula 
edid_block_status_valid(enum edid_block_status status,int tag)18911f221284SJani Nikula static bool edid_block_status_valid(enum edid_block_status status, int tag)
18921f221284SJani Nikula {
18931f221284SJani Nikula 	return status == EDID_BLOCK_OK ||
18941f221284SJani Nikula 		status == EDID_BLOCK_HEADER_FIXED ||
18951f221284SJani Nikula 		(status == EDID_BLOCK_CHECKSUM && tag == CEA_EXT);
18961f221284SJani Nikula }
18971f221284SJani Nikula 
edid_block_valid(const void * block,bool base)189823e38d7bSJani Nikula static bool edid_block_valid(const void *block, bool base)
189923e38d7bSJani Nikula {
190023e38d7bSJani Nikula 	return edid_block_status_valid(edid_block_check(block, base),
190123e38d7bSJani Nikula 				       edid_block_tag(block));
190223e38d7bSJani Nikula }
190323e38d7bSJani Nikula 
edid_block_status_print(enum edid_block_status status,const struct edid * block,int block_num)1904cee2ce1aSJani Nikula static void edid_block_status_print(enum edid_block_status status,
1905cee2ce1aSJani Nikula 				    const struct edid *block,
1906cee2ce1aSJani Nikula 				    int block_num)
1907cee2ce1aSJani Nikula {
1908cee2ce1aSJani Nikula 	switch (status) {
1909cee2ce1aSJani Nikula 	case EDID_BLOCK_OK:
1910cee2ce1aSJani Nikula 		break;
19112deaf1c2SJani Nikula 	case EDID_BLOCK_READ_FAIL:
19122deaf1c2SJani Nikula 		pr_debug("EDID block %d read failed\n", block_num);
19132deaf1c2SJani Nikula 		break;
1914cee2ce1aSJani Nikula 	case EDID_BLOCK_NULL:
1915cee2ce1aSJani Nikula 		pr_debug("EDID block %d pointer is NULL\n", block_num);
1916cee2ce1aSJani Nikula 		break;
1917cee2ce1aSJani Nikula 	case EDID_BLOCK_ZERO:
1918cee2ce1aSJani Nikula 		pr_notice("EDID block %d is all zeroes\n", block_num);
1919cee2ce1aSJani Nikula 		break;
1920cee2ce1aSJani Nikula 	case EDID_BLOCK_HEADER_CORRUPT:
1921cee2ce1aSJani Nikula 		pr_notice("EDID has corrupt header\n");
1922cee2ce1aSJani Nikula 		break;
1923cee2ce1aSJani Nikula 	case EDID_BLOCK_HEADER_REPAIR:
1924cee2ce1aSJani Nikula 		pr_debug("EDID corrupt header needs repair\n");
1925cee2ce1aSJani Nikula 		break;
1926cee2ce1aSJani Nikula 	case EDID_BLOCK_HEADER_FIXED:
1927cee2ce1aSJani Nikula 		pr_debug("EDID corrupt header fixed\n");
1928cee2ce1aSJani Nikula 		break;
1929cee2ce1aSJani Nikula 	case EDID_BLOCK_CHECKSUM:
1930cee2ce1aSJani Nikula 		if (edid_block_status_valid(status, edid_block_tag(block))) {
1931cee2ce1aSJani Nikula 			pr_debug("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d, ignoring\n",
1932cee2ce1aSJani Nikula 				 block_num, edid_block_tag(block),
1933cee2ce1aSJani Nikula 				 edid_block_compute_checksum(block));
1934cee2ce1aSJani Nikula 		} else {
1935cee2ce1aSJani Nikula 			pr_notice("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d\n",
1936cee2ce1aSJani Nikula 				  block_num, edid_block_tag(block),
1937cee2ce1aSJani Nikula 				  edid_block_compute_checksum(block));
1938cee2ce1aSJani Nikula 		}
1939cee2ce1aSJani Nikula 		break;
1940cee2ce1aSJani Nikula 	case EDID_BLOCK_VERSION:
1941cee2ce1aSJani Nikula 		pr_notice("EDID has major version %d, instead of 1\n",
1942cee2ce1aSJani Nikula 			  block->version);
1943cee2ce1aSJani Nikula 		break;
1944cee2ce1aSJani Nikula 	default:
1945cee2ce1aSJani Nikula 		WARN(1, "EDID block %d unknown edid block status code %d\n",
1946cee2ce1aSJani Nikula 		     block_num, status);
1947cee2ce1aSJani Nikula 		break;
1948cee2ce1aSJani Nikula 	}
1949cee2ce1aSJani Nikula }
1950cee2ce1aSJani Nikula 
edid_block_dump(const char * level,const void * block,int block_num)19519c7345deSJani Nikula static void edid_block_dump(const char *level, const void *block, int block_num)
19529c7345deSJani Nikula {
19539c7345deSJani Nikula 	enum edid_block_status status;
19549c7345deSJani Nikula 	char prefix[20];
19559c7345deSJani Nikula 
19569c7345deSJani Nikula 	status = edid_block_check(block, block_num == 0);
19579c7345deSJani Nikula 	if (status == EDID_BLOCK_ZERO)
19589c7345deSJani Nikula 		sprintf(prefix, "\t[%02x] ZERO ", block_num);
19599c7345deSJani Nikula 	else if (!edid_block_status_valid(status, edid_block_tag(block)))
19609c7345deSJani Nikula 		sprintf(prefix, "\t[%02x] BAD  ", block_num);
19619c7345deSJani Nikula 	else
19629c7345deSJani Nikula 		sprintf(prefix, "\t[%02x] GOOD ", block_num);
19639c7345deSJani Nikula 
19649c7345deSJani Nikula 	print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
19659c7345deSJani Nikula 		       block, EDID_LENGTH, false);
19669c7345deSJani Nikula }
19679c7345deSJani Nikula 
1968536faa45SStanislav Lisovskiy /**
1969db6cf833SThierry Reding  * drm_edid_block_valid - Sanity check the EDID block (base or extension)
19705d96fc9cSJani Nikula  * @_block: pointer to raw EDID block
19711f221284SJani Nikula  * @block_num: type of block to validate (0 for base, extension otherwise)
1972db6cf833SThierry Reding  * @print_bad_edid: if true, dump bad EDID blocks to the console
19736ba2bd3dSTodd Previte  * @edid_corrupt: if true, the header or checksum is invalid
1974db6cf833SThierry Reding  *
1975db6cf833SThierry Reding  * Validate a base or extension EDID block and optionally dump bad blocks to
1976db6cf833SThierry Reding  * the console.
1977db6cf833SThierry Reding  *
1978db6cf833SThierry Reding  * Return: True if the block is valid, false otherwise.
1979f453ba04SDave Airlie  */
drm_edid_block_valid(u8 * _block,int block_num,bool print_bad_edid,bool * edid_corrupt)19801f221284SJani Nikula bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
19816ba2bd3dSTodd Previte 			  bool *edid_corrupt)
1982f453ba04SDave Airlie {
19831f221284SJani Nikula 	struct edid *block = (struct edid *)_block;
19841f221284SJani Nikula 	enum edid_block_status status;
19851f221284SJani Nikula 	bool is_base_block = block_num == 0;
19861f221284SJani Nikula 	bool valid;
198761e57a8dSAdam Jackson 
19881f221284SJani Nikula 	if (WARN_ON(!block))
1989fe2ef780SSeung-Woo Kim 		return false;
1990fe2ef780SSeung-Woo Kim 
19911f221284SJani Nikula 	status = edid_block_check(block, is_base_block);
19921f221284SJani Nikula 	if (status == EDID_BLOCK_HEADER_REPAIR) {
1993e1e7bc48SJani Nikula 		DRM_DEBUG_KMS("Fixing EDID header, your hardware may be failing\n");
19941f221284SJani Nikula 		edid_header_fix(block);
19951f221284SJani Nikula 
19961f221284SJani Nikula 		/* Retry with fixed header, update status if that worked. */
19971f221284SJani Nikula 		status = edid_block_check(block, is_base_block);
19981f221284SJani Nikula 		if (status == EDID_BLOCK_OK)
19991f221284SJani Nikula 			status = EDID_BLOCK_HEADER_FIXED;
20001f221284SJani Nikula 	}
20011f221284SJani Nikula 
20021f221284SJani Nikula 	if (edid_corrupt) {
20031f221284SJani Nikula 		/*
20041f221284SJani Nikula 		 * Unknown major version isn't corrupt but we can't use it. Only
20051f221284SJani Nikula 		 * the base block can reset edid_corrupt to false.
20061f221284SJani Nikula 		 */
20071f221284SJani Nikula 		if (is_base_block &&
20081f221284SJani Nikula 		    (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION))
20091f221284SJani Nikula 			*edid_corrupt = false;
20101f221284SJani Nikula 		else if (status != EDID_BLOCK_OK)
2011ac6f2e29SDaniel Vetter 			*edid_corrupt = true;
201261e57a8dSAdam Jackson 	}
2013f453ba04SDave Airlie 
2014cee2ce1aSJani Nikula 	edid_block_status_print(status, block, block_num);
2015cee2ce1aSJani Nikula 
20161f221284SJani Nikula 	/* Determine whether we can use this block with this status. */
20171f221284SJani Nikula 	valid = edid_block_status_valid(status, edid_block_tag(block));
20186ba2bd3dSTodd Previte 
2019cee2ce1aSJani Nikula 	if (!valid && print_bad_edid && status != EDID_BLOCK_ZERO) {
2020499447dbSJoe Perches 		pr_notice("Raw EDID:\n");
20219c7345deSJani Nikula 		edid_block_dump(KERN_NOTICE, block, block_num);
2022f453ba04SDave Airlie 	}
20231f221284SJani Nikula 
20241f221284SJani Nikula 	return valid;
2025f453ba04SDave Airlie }
2026da0df92bSCarsten Emde EXPORT_SYMBOL(drm_edid_block_valid);
202761e57a8dSAdam Jackson 
202861e57a8dSAdam Jackson /**
202961e57a8dSAdam Jackson  * drm_edid_is_valid - sanity check EDID data
203061e57a8dSAdam Jackson  * @edid: EDID data
203161e57a8dSAdam Jackson  *
203261e57a8dSAdam Jackson  * Sanity-check an entire EDID record (including extensions)
2033db6cf833SThierry Reding  *
2034db6cf833SThierry Reding  * Return: True if the EDID data is valid, false otherwise.
203561e57a8dSAdam Jackson  */
drm_edid_is_valid(struct edid * edid)203661e57a8dSAdam Jackson bool drm_edid_is_valid(struct edid *edid)
203761e57a8dSAdam Jackson {
203861e57a8dSAdam Jackson 	int i;
203961e57a8dSAdam Jackson 
204061e57a8dSAdam Jackson 	if (!edid)
204161e57a8dSAdam Jackson 		return false;
204261e57a8dSAdam Jackson 
2043f1e4c916SJani Nikula 	for (i = 0; i < edid_block_count(edid); i++) {
2044f1e4c916SJani Nikula 		void *block = (void *)edid_block_data(edid, i);
2045f1e4c916SJani Nikula 
2046f1e4c916SJani Nikula 		if (!drm_edid_block_valid(block, i, true, NULL))
204761e57a8dSAdam Jackson 			return false;
2048f1e4c916SJani Nikula 	}
204961e57a8dSAdam Jackson 
205061e57a8dSAdam Jackson 	return true;
205161e57a8dSAdam Jackson }
20523c537889SAlex Deucher EXPORT_SYMBOL(drm_edid_is_valid);
2053f453ba04SDave Airlie 
20546c9b3db7SJani Nikula /**
20556c9b3db7SJani Nikula  * drm_edid_valid - sanity check EDID data
20566c9b3db7SJani Nikula  * @drm_edid: EDID data
20576c9b3db7SJani Nikula  *
20586c9b3db7SJani Nikula  * Sanity check an EDID. Cross check block count against allocated size and
20596c9b3db7SJani Nikula  * checksum the blocks.
20606c9b3db7SJani Nikula  *
20616c9b3db7SJani Nikula  * Return: True if the EDID data is valid, false otherwise.
20626c9b3db7SJani Nikula  */
drm_edid_valid(const struct drm_edid * drm_edid)20636c9b3db7SJani Nikula bool drm_edid_valid(const struct drm_edid *drm_edid)
20646c9b3db7SJani Nikula {
20656c9b3db7SJani Nikula 	int i;
20666c9b3db7SJani Nikula 
20676c9b3db7SJani Nikula 	if (!drm_edid)
20686c9b3db7SJani Nikula 		return false;
20696c9b3db7SJani Nikula 
20706c9b3db7SJani Nikula 	if (edid_size_by_blocks(__drm_edid_block_count(drm_edid)) != drm_edid->size)
20716c9b3db7SJani Nikula 		return false;
20726c9b3db7SJani Nikula 
20736c9b3db7SJani Nikula 	for (i = 0; i < drm_edid_block_count(drm_edid); i++) {
20746c9b3db7SJani Nikula 		const void *block = drm_edid_block_data(drm_edid, i);
20756c9b3db7SJani Nikula 
20766c9b3db7SJani Nikula 		if (!edid_block_valid(block, i == 0))
20776c9b3db7SJani Nikula 			return false;
20786c9b3db7SJani Nikula 	}
20796c9b3db7SJani Nikula 
20806c9b3db7SJani Nikula 	return true;
20816c9b3db7SJani Nikula }
20826c9b3db7SJani Nikula EXPORT_SYMBOL(drm_edid_valid);
20836c9b3db7SJani Nikula 
edid_filter_invalid_blocks(struct edid * edid,size_t * alloc_size)208489f4b4c5SJani Nikula static struct edid *edid_filter_invalid_blocks(struct edid *edid,
2085407d63b3SJani Nikula 					       size_t *alloc_size)
20864ec53461SJani Nikula {
208789f4b4c5SJani Nikula 	struct edid *new;
208889f4b4c5SJani Nikula 	int i, valid_blocks = 0;
20894ec53461SJani Nikula 
209018e3c1d5SJani Nikula 	/*
209118e3c1d5SJani Nikula 	 * Note: If the EDID uses HF-EEODB, but has invalid blocks, we'll revert
209218e3c1d5SJani Nikula 	 * back to regular extension count here. We don't want to start
209318e3c1d5SJani Nikula 	 * modifying the HF-EEODB extension too.
209418e3c1d5SJani Nikula 	 */
2095f1e4c916SJani Nikula 	for (i = 0; i < edid_block_count(edid); i++) {
209689f4b4c5SJani Nikula 		const void *src_block = edid_block_data(edid, i);
20974ec53461SJani Nikula 
209889f4b4c5SJani Nikula 		if (edid_block_valid(src_block, i == 0)) {
209989f4b4c5SJani Nikula 			void *dst_block = (void *)edid_block_data(edid, valid_blocks);
210089f4b4c5SJani Nikula 
210189f4b4c5SJani Nikula 			memmove(dst_block, src_block, EDID_LENGTH);
210289f4b4c5SJani Nikula 			valid_blocks++;
210389f4b4c5SJani Nikula 		}
21044ec53461SJani Nikula 	}
21054ec53461SJani Nikula 
210689f4b4c5SJani Nikula 	/* We already trusted the base block to be valid here... */
210789f4b4c5SJani Nikula 	if (WARN_ON(!valid_blocks)) {
210889f4b4c5SJani Nikula 		kfree(edid);
210989f4b4c5SJani Nikula 		return NULL;
211089f4b4c5SJani Nikula 	}
21114ec53461SJani Nikula 
211289f4b4c5SJani Nikula 	edid->extensions = valid_blocks - 1;
211389f4b4c5SJani Nikula 	edid->checksum = edid_block_compute_checksum(edid);
211489f4b4c5SJani Nikula 
211589f4b4c5SJani Nikula 	*alloc_size = edid_size_by_blocks(valid_blocks);
211689f4b4c5SJani Nikula 
211789f4b4c5SJani Nikula 	new = krealloc(edid, *alloc_size, GFP_KERNEL);
211889f4b4c5SJani Nikula 	if (!new)
21194ec53461SJani Nikula 		kfree(edid);
21204ec53461SJani Nikula 
21214ec53461SJani Nikula 	return new;
21224ec53461SJani Nikula }
21234ec53461SJani Nikula 
212461e57a8dSAdam Jackson #define DDC_SEGMENT_ADDR 0x30
212561e57a8dSAdam Jackson /**
2126db6cf833SThierry Reding  * drm_do_probe_ddc_edid() - get EDID information via I2C
21277c58e87eSThierry Reding  * @data: I2C device adapter
2128fc66811cSDaniel Vetter  * @buf: EDID data buffer to be filled
2129fc66811cSDaniel Vetter  * @block: 128 byte EDID block to start fetching from
2130fc66811cSDaniel Vetter  * @len: EDID data buffer length to fetch
2131fc66811cSDaniel Vetter  *
2132db6cf833SThierry Reding  * Try to fetch EDID information by calling I2C driver functions.
2133fc66811cSDaniel Vetter  *
2134db6cf833SThierry Reding  * Return: 0 on success or -1 on failure.
213561e57a8dSAdam Jackson  */
213661e57a8dSAdam Jackson static int
drm_do_probe_ddc_edid(void * data,u8 * buf,unsigned int block,size_t len)213718df89feSLars-Peter Clausen drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
213861e57a8dSAdam Jackson {
213918df89feSLars-Peter Clausen 	struct i2c_adapter *adapter = data;
214061e57a8dSAdam Jackson 	unsigned char start = block * EDID_LENGTH;
2141cd004b3fSShirish S 	unsigned char segment = block >> 1;
2142cd004b3fSShirish S 	unsigned char xfers = segment ? 3 : 2;
21434819d2e4SChris Wilson 	int ret, retries = 5;
21444819d2e4SChris Wilson 
2145db6cf833SThierry Reding 	/*
2146db6cf833SThierry Reding 	 * The core I2C driver will automatically retry the transfer if the
21474819d2e4SChris Wilson 	 * adapter reports EAGAIN. However, we find that bit-banging transfers
21484819d2e4SChris Wilson 	 * are susceptible to errors under a heavily loaded machine and
21494819d2e4SChris Wilson 	 * generate spurious NAKs and timeouts. Retrying the transfer
21504819d2e4SChris Wilson 	 * of the individual block a few times seems to overcome this.
21514819d2e4SChris Wilson 	 */
21524819d2e4SChris Wilson 	do {
215361e57a8dSAdam Jackson 		struct i2c_msg msgs[] = {
215461e57a8dSAdam Jackson 			{
2155cd004b3fSShirish S 				.addr	= DDC_SEGMENT_ADDR,
2156cd004b3fSShirish S 				.flags	= 0,
2157cd004b3fSShirish S 				.len	= 1,
2158cd004b3fSShirish S 				.buf	= &segment,
2159cd004b3fSShirish S 			}, {
216061e57a8dSAdam Jackson 				.addr	= DDC_ADDR,
216161e57a8dSAdam Jackson 				.flags	= 0,
216261e57a8dSAdam Jackson 				.len	= 1,
216361e57a8dSAdam Jackson 				.buf	= &start,
216461e57a8dSAdam Jackson 			}, {
216561e57a8dSAdam Jackson 				.addr	= DDC_ADDR,
216661e57a8dSAdam Jackson 				.flags	= I2C_M_RD,
216761e57a8dSAdam Jackson 				.len	= len,
21680ea75e23SSam Tygier 				.buf	= buf,
216961e57a8dSAdam Jackson 			}
217061e57a8dSAdam Jackson 		};
2171cd004b3fSShirish S 
2172cd004b3fSShirish S 		/*
2173db6cf833SThierry Reding 		 * Avoid sending the segment addr to not upset non-compliant
2174db6cf833SThierry Reding 		 * DDC monitors.
2175cd004b3fSShirish S 		 */
2176cd004b3fSShirish S 		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
2177cd004b3fSShirish S 
21789292f37eSEugeni Dodonov 		if (ret == -ENXIO) {
21799292f37eSEugeni Dodonov 			DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
21809292f37eSEugeni Dodonov 					adapter->name);
21819292f37eSEugeni Dodonov 			break;
21829292f37eSEugeni Dodonov 		}
2183cd004b3fSShirish S 	} while (ret != xfers && --retries);
218461e57a8dSAdam Jackson 
2185cd004b3fSShirish S 	return ret == xfers ? 0 : -1;
218661e57a8dSAdam Jackson }
218761e57a8dSAdam Jackson 
connector_bad_edid(struct drm_connector * connector,const struct edid * edid,int num_blocks)218814544d09SChris Wilson static void connector_bad_edid(struct drm_connector *connector,
218963cae081SJani Nikula 			       const struct edid *edid, int num_blocks)
219014544d09SChris Wilson {
219114544d09SChris Wilson 	int i;
219297794170SDouglas Anderson 	u8 last_block;
219397794170SDouglas Anderson 
219497794170SDouglas Anderson 	/*
219597794170SDouglas Anderson 	 * 0x7e in the EDID is the number of extension blocks. The EDID
219697794170SDouglas Anderson 	 * is 1 (base block) + num_ext_blocks big. That means we can think
219797794170SDouglas Anderson 	 * of 0x7e in the EDID of the _index_ of the last block in the
219897794170SDouglas Anderson 	 * combined chunk of memory.
219997794170SDouglas Anderson 	 */
220063cae081SJani Nikula 	last_block = edid->extensions;
2201e11f5bd8SJerry (Fangzhi) Zuo 
2202e11f5bd8SJerry (Fangzhi) Zuo 	/* Calculate real checksum for the last edid extension block data */
220397794170SDouglas Anderson 	if (last_block < num_blocks)
2204e11f5bd8SJerry (Fangzhi) Zuo 		connector->real_edid_checksum =
220563cae081SJani Nikula 			edid_block_compute_checksum(edid + last_block);
220614544d09SChris Wilson 
2207f0a8f533SJani Nikula 	if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
220814544d09SChris Wilson 		return;
220914544d09SChris Wilson 
221066d17ecdSJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID is invalid:\n",
221166d17ecdSJani Nikula 		    connector->base.id, connector->name);
221263cae081SJani Nikula 	for (i = 0; i < num_blocks; i++)
221363cae081SJani Nikula 		edid_block_dump(KERN_DEBUG, edid + i, i);
221414544d09SChris Wilson }
221514544d09SChris Wilson 
221656a2b7f2SJani Nikula /* Get override or firmware EDID */
drm_edid_override_get(struct drm_connector * connector)2217794aca0eSJani Nikula static const struct drm_edid *drm_edid_override_get(struct drm_connector *connector)
221856a2b7f2SJani Nikula {
2219794aca0eSJani Nikula 	const struct drm_edid *override = NULL;
222056a2b7f2SJani Nikula 
222190b575f5SJani Nikula 	mutex_lock(&connector->edid_override_mutex);
222290b575f5SJani Nikula 
222390b575f5SJani Nikula 	if (connector->edid_override)
2224794aca0eSJani Nikula 		override = drm_edid_dup(connector->edid_override);
222590b575f5SJani Nikula 
222690b575f5SJani Nikula 	mutex_unlock(&connector->edid_override_mutex);
222756a2b7f2SJani Nikula 
222856a2b7f2SJani Nikula 	if (!override)
2229a05992d5SJani Nikula 		override = drm_edid_load_firmware(connector);
223056a2b7f2SJani Nikula 
223156a2b7f2SJani Nikula 	return IS_ERR(override) ? NULL : override;
223256a2b7f2SJani Nikula }
223356a2b7f2SJani Nikula 
22346aa145bcSJani Nikula /* For debugfs edid_override implementation */
drm_edid_override_show(struct drm_connector * connector,struct seq_file * m)223591ec9ab4SJani Nikula int drm_edid_override_show(struct drm_connector *connector, struct seq_file *m)
223691ec9ab4SJani Nikula {
223790b575f5SJani Nikula 	const struct drm_edid *drm_edid;
223891ec9ab4SJani Nikula 
223990b575f5SJani Nikula 	mutex_lock(&connector->edid_override_mutex);
224090b575f5SJani Nikula 
224190b575f5SJani Nikula 	drm_edid = connector->edid_override;
224290b575f5SJani Nikula 	if (drm_edid)
224390b575f5SJani Nikula 		seq_write(m, drm_edid->edid, drm_edid->size);
224490b575f5SJani Nikula 
224590b575f5SJani Nikula 	mutex_unlock(&connector->edid_override_mutex);
224691ec9ab4SJani Nikula 
224791ec9ab4SJani Nikula 	return 0;
224891ec9ab4SJani Nikula }
224991ec9ab4SJani Nikula 
225091ec9ab4SJani Nikula /* For debugfs edid_override implementation */
drm_edid_override_set(struct drm_connector * connector,const void * edid,size_t size)22516aa145bcSJani Nikula int drm_edid_override_set(struct drm_connector *connector, const void *edid,
22526aa145bcSJani Nikula 			  size_t size)
22536aa145bcSJani Nikula {
225490b575f5SJani Nikula 	const struct drm_edid *drm_edid;
22556aa145bcSJani Nikula 
225690b575f5SJani Nikula 	drm_edid = drm_edid_alloc(edid, size);
225790b575f5SJani Nikula 	if (!drm_edid_valid(drm_edid)) {
225890b575f5SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override invalid\n",
225990b575f5SJani Nikula 			    connector->base.id, connector->name);
226090b575f5SJani Nikula 		drm_edid_free(drm_edid);
22616aa145bcSJani Nikula 		return -EINVAL;
226290b575f5SJani Nikula 	}
22636aa145bcSJani Nikula 
22642c9332deSJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override set\n",
22652c9332deSJani Nikula 		    connector->base.id, connector->name);
22662c9332deSJani Nikula 
226790b575f5SJani Nikula 	mutex_lock(&connector->edid_override_mutex);
22686aa145bcSJani Nikula 
226990b575f5SJani Nikula 	drm_edid_free(connector->edid_override);
227090b575f5SJani Nikula 	connector->edid_override = drm_edid;
227190b575f5SJani Nikula 
227290b575f5SJani Nikula 	mutex_unlock(&connector->edid_override_mutex);
227390b575f5SJani Nikula 
227490b575f5SJani Nikula 	return 0;
22756aa145bcSJani Nikula }
22766aa145bcSJani Nikula 
22776aa145bcSJani Nikula /* For debugfs edid_override implementation */
drm_edid_override_reset(struct drm_connector * connector)22786aa145bcSJani Nikula int drm_edid_override_reset(struct drm_connector *connector)
22796aa145bcSJani Nikula {
22802c9332deSJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override reset\n",
22812c9332deSJani Nikula 		    connector->base.id, connector->name);
22822c9332deSJani Nikula 
228390b575f5SJani Nikula 	mutex_lock(&connector->edid_override_mutex);
228490b575f5SJani Nikula 
228590b575f5SJani Nikula 	drm_edid_free(connector->edid_override);
228690b575f5SJani Nikula 	connector->edid_override = NULL;
228790b575f5SJani Nikula 
228890b575f5SJani Nikula 	mutex_unlock(&connector->edid_override_mutex);
228990b575f5SJani Nikula 
229090b575f5SJani Nikula 	return 0;
22916aa145bcSJani Nikula }
22926aa145bcSJani Nikula 
229318df89feSLars-Peter Clausen /**
2294019b9387SJani Nikula  * drm_edid_override_connector_update - add modes from override/firmware EDID
229548eaeb76SJani Nikula  * @connector: connector we're probing
229648eaeb76SJani Nikula  *
229748eaeb76SJani Nikula  * Add modes from the override/firmware EDID, if available. Only to be used from
229848eaeb76SJani Nikula  * drm_helper_probe_single_connector_modes() as a fallback for when DDC probe
229948eaeb76SJani Nikula  * failed during drm_get_edid() and caused the override/firmware EDID to be
230048eaeb76SJani Nikula  * skipped.
230148eaeb76SJani Nikula  *
230248eaeb76SJani Nikula  * Return: The number of modes added or 0 if we couldn't find any.
230348eaeb76SJani Nikula  */
drm_edid_override_connector_update(struct drm_connector * connector)2304019b9387SJani Nikula int drm_edid_override_connector_update(struct drm_connector *connector)
230548eaeb76SJani Nikula {
2306794aca0eSJani Nikula 	const struct drm_edid *override;
230748eaeb76SJani Nikula 	int num_modes = 0;
230848eaeb76SJani Nikula 
2309794aca0eSJani Nikula 	override = drm_edid_override_get(connector);
231048eaeb76SJani Nikula 	if (override) {
2311a511e851SJani Nikula 		if (drm_edid_connector_update(connector, override) == 0)
2312a511e851SJani Nikula 			num_modes = drm_edid_connector_add_modes(connector);
2313794aca0eSJani Nikula 
2314794aca0eSJani Nikula 		drm_edid_free(override);
231548eaeb76SJani Nikula 
2316e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev,
2317e1e7bc48SJani Nikula 			    "[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n",
231848eaeb76SJani Nikula 			    connector->base.id, connector->name, num_modes);
231948eaeb76SJani Nikula 	}
232048eaeb76SJani Nikula 
232148eaeb76SJani Nikula 	return num_modes;
232248eaeb76SJani Nikula }
2323019b9387SJani Nikula EXPORT_SYMBOL(drm_edid_override_connector_update);
232448eaeb76SJani Nikula 
232589fb7536SJani Nikula typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len);
232689fb7536SJani Nikula 
edid_block_read(void * block,unsigned int block_num,read_block_fn read_block,void * context)23272deaf1c2SJani Nikula static enum edid_block_status edid_block_read(void *block, unsigned int block_num,
23282deaf1c2SJani Nikula 					      read_block_fn read_block,
23292deaf1c2SJani Nikula 					      void *context)
23302deaf1c2SJani Nikula {
23312deaf1c2SJani Nikula 	enum edid_block_status status;
23322deaf1c2SJani Nikula 	bool is_base_block = block_num == 0;
23332deaf1c2SJani Nikula 	int try;
23342deaf1c2SJani Nikula 
23352deaf1c2SJani Nikula 	for (try = 0; try < 4; try++) {
23362deaf1c2SJani Nikula 		if (read_block(context, block, block_num, EDID_LENGTH))
23372deaf1c2SJani Nikula 			return EDID_BLOCK_READ_FAIL;
23382deaf1c2SJani Nikula 
23392deaf1c2SJani Nikula 		status = edid_block_check(block, is_base_block);
23402deaf1c2SJani Nikula 		if (status == EDID_BLOCK_HEADER_REPAIR) {
23412deaf1c2SJani Nikula 			edid_header_fix(block);
23422deaf1c2SJani Nikula 
23432deaf1c2SJani Nikula 			/* Retry with fixed header, update status if that worked. */
23442deaf1c2SJani Nikula 			status = edid_block_check(block, is_base_block);
23452deaf1c2SJani Nikula 			if (status == EDID_BLOCK_OK)
23462deaf1c2SJani Nikula 				status = EDID_BLOCK_HEADER_FIXED;
23472deaf1c2SJani Nikula 		}
23482deaf1c2SJani Nikula 
23492deaf1c2SJani Nikula 		if (edid_block_status_valid(status, edid_block_tag(block)))
23502deaf1c2SJani Nikula 			break;
23512deaf1c2SJani Nikula 
23522deaf1c2SJani Nikula 		/* Fail early for unrepairable base block all zeros. */
23532deaf1c2SJani Nikula 		if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO)
23542deaf1c2SJani Nikula 			break;
23552deaf1c2SJani Nikula 	}
23562deaf1c2SJani Nikula 
23572deaf1c2SJani Nikula 	return status;
23582deaf1c2SJani Nikula }
23592deaf1c2SJani Nikula 
_drm_do_get_edid(struct drm_connector * connector,read_block_fn read_block,void * context,size_t * size)23606537f79aSJani Nikula static struct edid *_drm_do_get_edid(struct drm_connector *connector,
23616537f79aSJani Nikula 				     read_block_fn read_block, void *context,
23626537f79aSJani Nikula 				     size_t *size)
236361e57a8dSAdam Jackson {
2364c12561ceSJani Nikula 	enum edid_block_status status;
236518e3c1d5SJani Nikula 	int i, num_blocks, invalid_blocks = 0;
2366794aca0eSJani Nikula 	const struct drm_edid *override;
2367b3eb97b6SJani Nikula 	struct edid *edid, *new;
2368407d63b3SJani Nikula 	size_t alloc_size = EDID_LENGTH;
236953fd40a9SJani Nikula 
2370794aca0eSJani Nikula 	override = drm_edid_override_get(connector);
2371794aca0eSJani Nikula 	if (override) {
2372794aca0eSJani Nikula 		alloc_size = override->size;
2373794aca0eSJani Nikula 		edid = kmemdup(override->edid, alloc_size, GFP_KERNEL);
2374794aca0eSJani Nikula 		drm_edid_free(override);
2375794aca0eSJani Nikula 		if (!edid)
2376794aca0eSJani Nikula 			return NULL;
23771c788f69SJani Nikula 		goto ok;
2378794aca0eSJani Nikula 	}
237961e57a8dSAdam Jackson 
2380407d63b3SJani Nikula 	edid = kmalloc(alloc_size, GFP_KERNEL);
2381e7bd95a7SDouglas Anderson 	if (!edid)
238261e57a8dSAdam Jackson 		return NULL;
238361e57a8dSAdam Jackson 
2384c12561ceSJani Nikula 	status = edid_block_read(edid, 0, read_block, context);
2385c12561ceSJani Nikula 
2386c12561ceSJani Nikula 	edid_block_status_print(status, edid, 0);
2387c12561ceSJani Nikula 
2388c12561ceSJani Nikula 	if (status == EDID_BLOCK_READ_FAIL)
23891c788f69SJani Nikula 		goto fail;
2390c12561ceSJani Nikula 
2391c12561ceSJani Nikula 	/* FIXME: Clarify what a corrupt EDID actually means. */
2392c12561ceSJani Nikula 	if (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION)
2393c12561ceSJani Nikula 		connector->edid_corrupt = false;
2394c12561ceSJani Nikula 	else
2395c12561ceSJani Nikula 		connector->edid_corrupt = true;
2396c12561ceSJani Nikula 
2397c12561ceSJani Nikula 	if (!edid_block_status_valid(status, edid_block_tag(edid))) {
2398c12561ceSJani Nikula 		if (status == EDID_BLOCK_ZERO)
2399c12561ceSJani Nikula 			connector->null_edid_counter++;
2400c12561ceSJani Nikula 
2401c12561ceSJani Nikula 		connector_bad_edid(connector, edid, 1);
24021c788f69SJani Nikula 		goto fail;
2403c12561ceSJani Nikula 	}
2404c12561ceSJani Nikula 
2405f1e4c916SJani Nikula 	if (!edid_extension_block_count(edid))
24061c788f69SJani Nikula 		goto ok;
240761e57a8dSAdam Jackson 
2408407d63b3SJani Nikula 	alloc_size = edid_size(edid);
2409407d63b3SJani Nikula 	new = krealloc(edid, alloc_size, GFP_KERNEL);
241061e57a8dSAdam Jackson 	if (!new)
24111c788f69SJani Nikula 		goto fail;
2412f14f3686SChris Wilson 	edid = new;
241361e57a8dSAdam Jackson 
241418e3c1d5SJani Nikula 	num_blocks = edid_block_count(edid);
241518e3c1d5SJani Nikula 	for (i = 1; i < num_blocks; i++) {
2416f1e4c916SJani Nikula 		void *block = (void *)edid_block_data(edid, i);
2417a28187ccSChris Wilson 
2418f1e4c916SJani Nikula 		status = edid_block_read(block, i, read_block, context);
2419d3da3f40SJani Nikula 
2420f1e4c916SJani Nikula 		edid_block_status_print(status, block, i);
2421d3da3f40SJani Nikula 
2422d3da3f40SJani Nikula 		if (!edid_block_status_valid(status, edid_block_tag(block))) {
2423d3da3f40SJani Nikula 			if (status == EDID_BLOCK_READ_FAIL)
24241c788f69SJani Nikula 				goto fail;
2425ccc97defSJani Nikula 			invalid_blocks++;
242618e3c1d5SJani Nikula 		} else if (i == 1) {
242718e3c1d5SJani Nikula 			/*
242818e3c1d5SJani Nikula 			 * If the first EDID extension is a CTA extension, and
242918e3c1d5SJani Nikula 			 * the first Data Block is HF-EEODB, override the
243018e3c1d5SJani Nikula 			 * extension block count.
243118e3c1d5SJani Nikula 			 *
243218e3c1d5SJani Nikula 			 * Note: HF-EEODB could specify a smaller extension
243318e3c1d5SJani Nikula 			 * count too, but we can't risk allocating a smaller
243418e3c1d5SJani Nikula 			 * amount.
243518e3c1d5SJani Nikula 			 */
243618e3c1d5SJani Nikula 			int eeodb = edid_hfeeodb_block_count(edid);
243718e3c1d5SJani Nikula 
243818e3c1d5SJani Nikula 			if (eeodb > num_blocks) {
243918e3c1d5SJani Nikula 				num_blocks = eeodb;
244018e3c1d5SJani Nikula 				alloc_size = edid_size_by_blocks(num_blocks);
244118e3c1d5SJani Nikula 				new = krealloc(edid, alloc_size, GFP_KERNEL);
244218e3c1d5SJani Nikula 				if (!new)
244318e3c1d5SJani Nikula 					goto fail;
244418e3c1d5SJani Nikula 				edid = new;
244518e3c1d5SJani Nikula 			}
24460ea75e23SSam Tygier 		}
2447d3da3f40SJani Nikula 	}
24480ea75e23SSam Tygier 
2449ccc97defSJani Nikula 	if (invalid_blocks) {
245018e3c1d5SJani Nikula 		connector_bad_edid(connector, edid, num_blocks);
245114544d09SChris Wilson 
245289f4b4c5SJani Nikula 		edid = edid_filter_invalid_blocks(edid, &alloc_size);
245361e57a8dSAdam Jackson 	}
245461e57a8dSAdam Jackson 
24551c788f69SJani Nikula ok:
24566537f79aSJani Nikula 	if (size)
24576537f79aSJani Nikula 		*size = alloc_size;
24586537f79aSJani Nikula 
2459e9a9e076SJani Nikula 	return edid;
246061e57a8dSAdam Jackson 
24611c788f69SJani Nikula fail:
2462f14f3686SChris Wilson 	kfree(edid);
246361e57a8dSAdam Jackson 	return NULL;
246461e57a8dSAdam Jackson }
24656537f79aSJani Nikula 
24666537f79aSJani Nikula /**
24676537f79aSJani Nikula  * drm_do_get_edid - get EDID data using a custom EDID block read function
24686537f79aSJani Nikula  * @connector: connector we're probing
24696537f79aSJani Nikula  * @read_block: EDID block read function
24706537f79aSJani Nikula  * @context: private data passed to the block read function
24716537f79aSJani Nikula  *
24726537f79aSJani Nikula  * When the I2C adapter connected to the DDC bus is hidden behind a device that
24736537f79aSJani Nikula  * exposes a different interface to read EDID blocks this function can be used
24746537f79aSJani Nikula  * to get EDID data using a custom block read function.
24756537f79aSJani Nikula  *
24766537f79aSJani Nikula  * As in the general case the DDC bus is accessible by the kernel at the I2C
24776537f79aSJani Nikula  * level, drivers must make all reasonable efforts to expose it as an I2C
24786537f79aSJani Nikula  * adapter and use drm_get_edid() instead of abusing this function.
24796537f79aSJani Nikula  *
24806537f79aSJani Nikula  * The EDID may be overridden using debugfs override_edid or firmware EDID
2481a05992d5SJani Nikula  * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
24826537f79aSJani Nikula  * order. Having either of them bypasses actual EDID reads.
24836537f79aSJani Nikula  *
24846537f79aSJani Nikula  * Return: Pointer to valid EDID or NULL if we couldn't find any.
24856537f79aSJani Nikula  */
drm_do_get_edid(struct drm_connector * connector,read_block_fn read_block,void * context)24866537f79aSJani Nikula struct edid *drm_do_get_edid(struct drm_connector *connector,
24876537f79aSJani Nikula 			     read_block_fn read_block,
24886537f79aSJani Nikula 			     void *context)
24896537f79aSJani Nikula {
24906537f79aSJani Nikula 	return _drm_do_get_edid(connector, read_block, context, NULL);
24916537f79aSJani Nikula }
249218df89feSLars-Peter Clausen EXPORT_SYMBOL_GPL(drm_do_get_edid);
249361e57a8dSAdam Jackson 
24943d1ab66eSJani Nikula /**
24953d1ab66eSJani Nikula  * drm_edid_raw - Get a pointer to the raw EDID data.
24963d1ab66eSJani Nikula  * @drm_edid: drm_edid container
24973d1ab66eSJani Nikula  *
24983d1ab66eSJani Nikula  * Get a pointer to the raw EDID data.
24993d1ab66eSJani Nikula  *
25003d1ab66eSJani Nikula  * This is for transition only. Avoid using this like the plague.
25013d1ab66eSJani Nikula  *
25023d1ab66eSJani Nikula  * Return: Pointer to raw EDID data.
25033d1ab66eSJani Nikula  */
drm_edid_raw(const struct drm_edid * drm_edid)25043d1ab66eSJani Nikula const struct edid *drm_edid_raw(const struct drm_edid *drm_edid)
25053d1ab66eSJani Nikula {
25063d1ab66eSJani Nikula 	if (!drm_edid || !drm_edid->size)
25073d1ab66eSJani Nikula 		return NULL;
25083d1ab66eSJani Nikula 
25093d1ab66eSJani Nikula 	/*
25103d1ab66eSJani Nikula 	 * Do not return pointers where relying on EDID extension count would
25113d1ab66eSJani Nikula 	 * lead to buffer overflow.
25123d1ab66eSJani Nikula 	 */
25133d1ab66eSJani Nikula 	if (WARN_ON(edid_size(drm_edid->edid) > drm_edid->size))
25143d1ab66eSJani Nikula 		return NULL;
25153d1ab66eSJani Nikula 
25163d1ab66eSJani Nikula 	return drm_edid->edid;
25173d1ab66eSJani Nikula }
25183d1ab66eSJani Nikula EXPORT_SYMBOL(drm_edid_raw);
25193d1ab66eSJani Nikula 
25206537f79aSJani Nikula /* Allocate struct drm_edid container *without* duplicating the edid data */
_drm_edid_alloc(const void * edid,size_t size)25216537f79aSJani Nikula static const struct drm_edid *_drm_edid_alloc(const void *edid, size_t size)
25226537f79aSJani Nikula {
25236537f79aSJani Nikula 	struct drm_edid *drm_edid;
25246537f79aSJani Nikula 
25256537f79aSJani Nikula 	if (!edid || !size || size < EDID_LENGTH)
25266537f79aSJani Nikula 		return NULL;
25276537f79aSJani Nikula 
25286537f79aSJani Nikula 	drm_edid = kzalloc(sizeof(*drm_edid), GFP_KERNEL);
25296537f79aSJani Nikula 	if (drm_edid) {
25306537f79aSJani Nikula 		drm_edid->edid = edid;
25316537f79aSJani Nikula 		drm_edid->size = size;
25326537f79aSJani Nikula 	}
25336537f79aSJani Nikula 
25346537f79aSJani Nikula 	return drm_edid;
25356537f79aSJani Nikula }
25366537f79aSJani Nikula 
25376537f79aSJani Nikula /**
25386537f79aSJani Nikula  * drm_edid_alloc - Allocate a new drm_edid container
25396537f79aSJani Nikula  * @edid: Pointer to raw EDID data
25406537f79aSJani Nikula  * @size: Size of memory allocated for EDID
25416537f79aSJani Nikula  *
25426537f79aSJani Nikula  * Allocate a new drm_edid container. Do not calculate edid size from edid, pass
25436537f79aSJani Nikula  * the actual size that has been allocated for the data. There is no validation
25446537f79aSJani Nikula  * of the raw EDID data against the size, but at least the EDID base block must
25456537f79aSJani Nikula  * fit in the buffer.
25466537f79aSJani Nikula  *
25476537f79aSJani Nikula  * The returned pointer must be freed using drm_edid_free().
25486537f79aSJani Nikula  *
25496537f79aSJani Nikula  * Return: drm_edid container, or NULL on errors
25506537f79aSJani Nikula  */
drm_edid_alloc(const void * edid,size_t size)25516537f79aSJani Nikula const struct drm_edid *drm_edid_alloc(const void *edid, size_t size)
25526537f79aSJani Nikula {
25536537f79aSJani Nikula 	const struct drm_edid *drm_edid;
25546537f79aSJani Nikula 
25556537f79aSJani Nikula 	if (!edid || !size || size < EDID_LENGTH)
25566537f79aSJani Nikula 		return NULL;
25576537f79aSJani Nikula 
25586537f79aSJani Nikula 	edid = kmemdup(edid, size, GFP_KERNEL);
25596537f79aSJani Nikula 	if (!edid)
25606537f79aSJani Nikula 		return NULL;
25616537f79aSJani Nikula 
25626537f79aSJani Nikula 	drm_edid = _drm_edid_alloc(edid, size);
25636537f79aSJani Nikula 	if (!drm_edid)
25646537f79aSJani Nikula 		kfree(edid);
25656537f79aSJani Nikula 
25666537f79aSJani Nikula 	return drm_edid;
25676537f79aSJani Nikula }
25686537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_alloc);
25696537f79aSJani Nikula 
25706537f79aSJani Nikula /**
25716537f79aSJani Nikula  * drm_edid_dup - Duplicate a drm_edid container
25726537f79aSJani Nikula  * @drm_edid: EDID to duplicate
25736537f79aSJani Nikula  *
25746537f79aSJani Nikula  * The returned pointer must be freed using drm_edid_free().
25756537f79aSJani Nikula  *
25766537f79aSJani Nikula  * Returns: drm_edid container copy, or NULL on errors
25776537f79aSJani Nikula  */
drm_edid_dup(const struct drm_edid * drm_edid)25786537f79aSJani Nikula const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid)
25796537f79aSJani Nikula {
25806537f79aSJani Nikula 	if (!drm_edid)
25816537f79aSJani Nikula 		return NULL;
25826537f79aSJani Nikula 
25836537f79aSJani Nikula 	return drm_edid_alloc(drm_edid->edid, drm_edid->size);
25846537f79aSJani Nikula }
25856537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_dup);
25866537f79aSJani Nikula 
25876537f79aSJani Nikula /**
25886537f79aSJani Nikula  * drm_edid_free - Free the drm_edid container
25896537f79aSJani Nikula  * @drm_edid: EDID to free
25906537f79aSJani Nikula  */
drm_edid_free(const struct drm_edid * drm_edid)25916537f79aSJani Nikula void drm_edid_free(const struct drm_edid *drm_edid)
25926537f79aSJani Nikula {
25936537f79aSJani Nikula 	if (!drm_edid)
25946537f79aSJani Nikula 		return;
25956537f79aSJani Nikula 
25966537f79aSJani Nikula 	kfree(drm_edid->edid);
25976537f79aSJani Nikula 	kfree(drm_edid);
25986537f79aSJani Nikula }
25996537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_free);
26006537f79aSJani Nikula 
260161e57a8dSAdam Jackson /**
2602db6cf833SThierry Reding  * drm_probe_ddc() - probe DDC presence
2603db6cf833SThierry Reding  * @adapter: I2C adapter to probe
260461e57a8dSAdam Jackson  *
2605db6cf833SThierry Reding  * Return: True on success, false on failure.
260661e57a8dSAdam Jackson  */
2607fbff4690SAdam Jackson bool
drm_probe_ddc(struct i2c_adapter * adapter)260861e57a8dSAdam Jackson drm_probe_ddc(struct i2c_adapter *adapter)
260961e57a8dSAdam Jackson {
261061e57a8dSAdam Jackson 	unsigned char out;
261161e57a8dSAdam Jackson 
261261e57a8dSAdam Jackson 	return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0);
261361e57a8dSAdam Jackson }
2614fbff4690SAdam Jackson EXPORT_SYMBOL(drm_probe_ddc);
261561e57a8dSAdam Jackson 
261661e57a8dSAdam Jackson /**
261761e57a8dSAdam Jackson  * drm_get_edid - get EDID data, if available
261861e57a8dSAdam Jackson  * @connector: connector we're probing
2619db6cf833SThierry Reding  * @adapter: I2C adapter to use for DDC
262061e57a8dSAdam Jackson  *
2621db6cf833SThierry Reding  * Poke the given I2C channel to grab EDID data if possible.  If found,
262261e57a8dSAdam Jackson  * attach it to the connector.
262361e57a8dSAdam Jackson  *
2624db6cf833SThierry Reding  * Return: Pointer to valid EDID or NULL if we couldn't find any.
262561e57a8dSAdam Jackson  */
drm_get_edid(struct drm_connector * connector,struct i2c_adapter * adapter)262661e57a8dSAdam Jackson struct edid *drm_get_edid(struct drm_connector *connector,
262761e57a8dSAdam Jackson 			  struct i2c_adapter *adapter)
262861e57a8dSAdam Jackson {
26295186421cSStanislav Lisovskiy 	struct edid *edid;
26305186421cSStanislav Lisovskiy 
263115f080f0SJani Nikula 	if (connector->force == DRM_FORCE_OFF)
263215f080f0SJani Nikula 		return NULL;
263315f080f0SJani Nikula 
263415f080f0SJani Nikula 	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
263518df89feSLars-Peter Clausen 		return NULL;
263661e57a8dSAdam Jackson 
26376537f79aSJani Nikula 	edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);
26385186421cSStanislav Lisovskiy 	drm_connector_update_edid_property(connector, edid);
26395186421cSStanislav Lisovskiy 	return edid;
264061e57a8dSAdam Jackson }
264161e57a8dSAdam Jackson EXPORT_SYMBOL(drm_get_edid);
264261e57a8dSAdam Jackson 
26436537f79aSJani Nikula /**
26446537f79aSJani Nikula  * drm_edid_read_custom - Read EDID data using given EDID block read function
26456537f79aSJani Nikula  * @connector: Connector to use
26466537f79aSJani Nikula  * @read_block: EDID block read function
26476537f79aSJani Nikula  * @context: Private data passed to the block read function
26486537f79aSJani Nikula  *
26496537f79aSJani Nikula  * When the I2C adapter connected to the DDC bus is hidden behind a device that
26506537f79aSJani Nikula  * exposes a different interface to read EDID blocks this function can be used
26516537f79aSJani Nikula  * to get EDID data using a custom block read function.
26526537f79aSJani Nikula  *
26536537f79aSJani Nikula  * As in the general case the DDC bus is accessible by the kernel at the I2C
26546537f79aSJani Nikula  * level, drivers must make all reasonable efforts to expose it as an I2C
26556537f79aSJani Nikula  * adapter and use drm_edid_read() or drm_edid_read_ddc() instead of abusing
26566537f79aSJani Nikula  * this function.
26576537f79aSJani Nikula  *
26586537f79aSJani Nikula  * The EDID may be overridden using debugfs override_edid or firmware EDID
2659a05992d5SJani Nikula  * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
26606537f79aSJani Nikula  * order. Having either of them bypasses actual EDID reads.
26616537f79aSJani Nikula  *
26626537f79aSJani Nikula  * The returned pointer must be freed using drm_edid_free().
26636537f79aSJani Nikula  *
26646537f79aSJani Nikula  * Return: Pointer to EDID, or NULL if probe/read failed.
26656537f79aSJani Nikula  */
drm_edid_read_custom(struct drm_connector * connector,read_block_fn read_block,void * context)26666537f79aSJani Nikula const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector,
26676537f79aSJani Nikula 					    read_block_fn read_block,
26686537f79aSJani Nikula 					    void *context)
26696537f79aSJani Nikula {
26706537f79aSJani Nikula 	const struct drm_edid *drm_edid;
26716537f79aSJani Nikula 	struct edid *edid;
26726537f79aSJani Nikula 	size_t size = 0;
26736537f79aSJani Nikula 
26746537f79aSJani Nikula 	edid = _drm_do_get_edid(connector, read_block, context, &size);
26756537f79aSJani Nikula 	if (!edid)
26766537f79aSJani Nikula 		return NULL;
26776537f79aSJani Nikula 
26786537f79aSJani Nikula 	/* Sanity check for now */
26796537f79aSJani Nikula 	drm_WARN_ON(connector->dev, !size);
26806537f79aSJani Nikula 
26816537f79aSJani Nikula 	drm_edid = _drm_edid_alloc(edid, size);
26826537f79aSJani Nikula 	if (!drm_edid)
26836537f79aSJani Nikula 		kfree(edid);
26846537f79aSJani Nikula 
26856537f79aSJani Nikula 	return drm_edid;
26866537f79aSJani Nikula }
26876537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_read_custom);
26886537f79aSJani Nikula 
26896537f79aSJani Nikula /**
26906537f79aSJani Nikula  * drm_edid_read_ddc - Read EDID data using given I2C adapter
26916537f79aSJani Nikula  * @connector: Connector to use
26926537f79aSJani Nikula  * @adapter: I2C adapter to use for DDC
26936537f79aSJani Nikula  *
26946537f79aSJani Nikula  * Read EDID using the given I2C adapter.
26956537f79aSJani Nikula  *
26966537f79aSJani Nikula  * The EDID may be overridden using debugfs override_edid or firmware EDID
2697a05992d5SJani Nikula  * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
26986537f79aSJani Nikula  * order. Having either of them bypasses actual EDID reads.
26996537f79aSJani Nikula  *
27006537f79aSJani Nikula  * Prefer initializing connector->ddc with drm_connector_init_with_ddc() and
27016537f79aSJani Nikula  * using drm_edid_read() instead of this function.
27026537f79aSJani Nikula  *
27036537f79aSJani Nikula  * The returned pointer must be freed using drm_edid_free().
27046537f79aSJani Nikula  *
27056537f79aSJani Nikula  * Return: Pointer to EDID, or NULL if probe/read failed.
27066537f79aSJani Nikula  */
drm_edid_read_ddc(struct drm_connector * connector,struct i2c_adapter * adapter)27076537f79aSJani Nikula const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
27086537f79aSJani Nikula 					 struct i2c_adapter *adapter)
27096537f79aSJani Nikula {
27106537f79aSJani Nikula 	const struct drm_edid *drm_edid;
27116537f79aSJani Nikula 
27126537f79aSJani Nikula 	if (connector->force == DRM_FORCE_OFF)
27136537f79aSJani Nikula 		return NULL;
27146537f79aSJani Nikula 
27156537f79aSJani Nikula 	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
27166537f79aSJani Nikula 		return NULL;
27176537f79aSJani Nikula 
27186537f79aSJani Nikula 	drm_edid = drm_edid_read_custom(connector, drm_do_probe_ddc_edid, adapter);
27196537f79aSJani Nikula 
27206537f79aSJani Nikula 	/* Note: Do *not* call connector updates here. */
27216537f79aSJani Nikula 
27226537f79aSJani Nikula 	return drm_edid;
27236537f79aSJani Nikula }
27246537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_read_ddc);
27256537f79aSJani Nikula 
27266537f79aSJani Nikula /**
27276537f79aSJani Nikula  * drm_edid_read - Read EDID data using connector's I2C adapter
27286537f79aSJani Nikula  * @connector: Connector to use
27296537f79aSJani Nikula  *
27306537f79aSJani Nikula  * Read EDID using the connector's I2C adapter.
27316537f79aSJani Nikula  *
27326537f79aSJani Nikula  * The EDID may be overridden using debugfs override_edid or firmware EDID
2733a05992d5SJani Nikula  * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
27346537f79aSJani Nikula  * order. Having either of them bypasses actual EDID reads.
27356537f79aSJani Nikula  *
27366537f79aSJani Nikula  * The returned pointer must be freed using drm_edid_free().
27376537f79aSJani Nikula  *
27386537f79aSJani Nikula  * Return: Pointer to EDID, or NULL if probe/read failed.
27396537f79aSJani Nikula  */
drm_edid_read(struct drm_connector * connector)27406537f79aSJani Nikula const struct drm_edid *drm_edid_read(struct drm_connector *connector)
27416537f79aSJani Nikula {
27426537f79aSJani Nikula 	if (drm_WARN_ON(connector->dev, !connector->ddc))
27436537f79aSJani Nikula 		return NULL;
27446537f79aSJani Nikula 
27456537f79aSJani Nikula 	return drm_edid_read_ddc(connector, connector->ddc);
27466537f79aSJani Nikula }
27476537f79aSJani Nikula EXPORT_SYMBOL(drm_edid_read);
27486537f79aSJani Nikula 
edid_extract_panel_id(const struct edid * edid)2749d9f91a10SDouglas Anderson static u32 edid_extract_panel_id(const struct edid *edid)
2750d9f91a10SDouglas Anderson {
2751d9f91a10SDouglas Anderson 	/*
2752e8de4d55SDouglas Anderson 	 * We represent the ID as a 32-bit number so it can easily be compared
2753e8de4d55SDouglas Anderson 	 * with "==".
2754d9f91a10SDouglas Anderson 	 *
2755d9f91a10SDouglas Anderson 	 * NOTE that we deal with endianness differently for the top half
2756d9f91a10SDouglas Anderson 	 * of this ID than for the bottom half. The bottom half (the product
2757d9f91a10SDouglas Anderson 	 * id) gets decoded as little endian by the EDID_PRODUCT_ID because
2758d9f91a10SDouglas Anderson 	 * that's how everyone seems to interpret it. The top half (the mfg_id)
2759d9f91a10SDouglas Anderson 	 * gets stored as big endian because that makes
2760d9f91a10SDouglas Anderson 	 * drm_edid_encode_panel_id() and drm_edid_decode_panel_id() easier
2761d9f91a10SDouglas Anderson 	 * to write (it's easier to extract the ASCII). It doesn't really
2762d9f91a10SDouglas Anderson 	 * matter, though, as long as the number here is unique.
2763d9f91a10SDouglas Anderson 	 */
2764d9f91a10SDouglas Anderson 	return (u32)edid->mfg_id[0] << 24   |
2765d9f91a10SDouglas Anderson 	       (u32)edid->mfg_id[1] << 16   |
2766d9f91a10SDouglas Anderson 	       (u32)EDID_PRODUCT_ID(edid);
2767d9f91a10SDouglas Anderson }
2768d9f91a10SDouglas Anderson 
2769d9f91a10SDouglas Anderson /**
2770d9f91a10SDouglas Anderson  * drm_edid_get_panel_id - Get a panel's ID through DDC
2771d9f91a10SDouglas Anderson  * @adapter: I2C adapter to use for DDC
2772d9f91a10SDouglas Anderson  *
2773d9f91a10SDouglas Anderson  * This function reads the first block of the EDID of a panel and (assuming
2774d9f91a10SDouglas Anderson  * that the EDID is valid) extracts the ID out of it. The ID is a 32-bit value
2775d9f91a10SDouglas Anderson  * (16 bits of manufacturer ID and 16 bits of per-manufacturer ID) that's
2776d9f91a10SDouglas Anderson  * supposed to be different for each different modem of panel.
2777d9f91a10SDouglas Anderson  *
2778d9f91a10SDouglas Anderson  * This function is intended to be used during early probing on devices where
2779d9f91a10SDouglas Anderson  * more than one panel might be present. Because of its intended use it must
2780d9f91a10SDouglas Anderson  * assume that the EDID of the panel is correct, at least as far as the ID
2781d9f91a10SDouglas Anderson  * is concerned (in other words, we don't process any overrides here).
2782d9f91a10SDouglas Anderson  *
2783d9f91a10SDouglas Anderson  * NOTE: it's expected that this function and drm_do_get_edid() will both
2784d9f91a10SDouglas Anderson  * be read the EDID, but there is no caching between them. Since we're only
2785d9f91a10SDouglas Anderson  * reading the first block, hopefully this extra overhead won't be too big.
2786d9f91a10SDouglas Anderson  *
2787d9f91a10SDouglas Anderson  * Return: A 32-bit ID that should be different for each make/model of panel.
2788d9f91a10SDouglas Anderson  *         See the functions drm_edid_encode_panel_id() and
2789d9f91a10SDouglas Anderson  *         drm_edid_decode_panel_id() for some details on the structure of this
2790d9f91a10SDouglas Anderson  *         ID.
2791d9f91a10SDouglas Anderson  */
2792d9f91a10SDouglas Anderson 
drm_edid_get_panel_id(struct i2c_adapter * adapter)2793d9f91a10SDouglas Anderson u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
2794d9f91a10SDouglas Anderson {
27952deaf1c2SJani Nikula 	enum edid_block_status status;
27962deaf1c2SJani Nikula 	void *base_block;
27972deaf1c2SJani Nikula 	u32 panel_id = 0;
2798d9f91a10SDouglas Anderson 
2799d9f91a10SDouglas Anderson 	/*
2800d9f91a10SDouglas Anderson 	 * There are no manufacturer IDs of 0, so if there is a problem reading
2801d9f91a10SDouglas Anderson 	 * the EDID then we'll just return 0.
2802d9f91a10SDouglas Anderson 	 */
28032deaf1c2SJani Nikula 
28044d8457feSJohan Hovold 	base_block = kzalloc(EDID_LENGTH, GFP_KERNEL);
28052deaf1c2SJani Nikula 	if (!base_block)
2806d9f91a10SDouglas Anderson 		return 0;
2807d9f91a10SDouglas Anderson 
28082deaf1c2SJani Nikula 	status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter);
28092deaf1c2SJani Nikula 
28102deaf1c2SJani Nikula 	edid_block_status_print(status, base_block, 0);
28112deaf1c2SJani Nikula 
28122deaf1c2SJani Nikula 	if (edid_block_status_valid(status, edid_block_tag(base_block)))
28132deaf1c2SJani Nikula 		panel_id = edid_extract_panel_id(base_block);
281469c7717cSDouglas Anderson 	else
281569c7717cSDouglas Anderson 		edid_block_dump(KERN_NOTICE, base_block, 0);
28162deaf1c2SJani Nikula 
28172deaf1c2SJani Nikula 	kfree(base_block);
2818d9f91a10SDouglas Anderson 
2819d9f91a10SDouglas Anderson 	return panel_id;
2820d9f91a10SDouglas Anderson }
2821d9f91a10SDouglas Anderson EXPORT_SYMBOL(drm_edid_get_panel_id);
2822d9f91a10SDouglas Anderson 
282351f8da59SJani Nikula /**
28245cb8eaa2SLukas Wunner  * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output
28255cb8eaa2SLukas Wunner  * @connector: connector we're probing
28265cb8eaa2SLukas Wunner  * @adapter: I2C adapter to use for DDC
28275cb8eaa2SLukas Wunner  *
28285cb8eaa2SLukas Wunner  * Wrapper around drm_get_edid() for laptops with dual GPUs using one set of
28295cb8eaa2SLukas Wunner  * outputs. The wrapper adds the requisite vga_switcheroo calls to temporarily
28305cb8eaa2SLukas Wunner  * switch DDC to the GPU which is retrieving EDID.
28315cb8eaa2SLukas Wunner  *
28325cb8eaa2SLukas Wunner  * Return: Pointer to valid EDID or %NULL if we couldn't find any.
28335cb8eaa2SLukas Wunner  */
drm_get_edid_switcheroo(struct drm_connector * connector,struct i2c_adapter * adapter)28345cb8eaa2SLukas Wunner struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
28355cb8eaa2SLukas Wunner 				     struct i2c_adapter *adapter)
28365cb8eaa2SLukas Wunner {
283736b73b05SThomas Zimmermann 	struct drm_device *dev = connector->dev;
283836b73b05SThomas Zimmermann 	struct pci_dev *pdev = to_pci_dev(dev->dev);
28395cb8eaa2SLukas Wunner 	struct edid *edid;
28405cb8eaa2SLukas Wunner 
284136b73b05SThomas Zimmermann 	if (drm_WARN_ON_ONCE(dev, !dev_is_pci(dev->dev)))
284236b73b05SThomas Zimmermann 		return NULL;
284336b73b05SThomas Zimmermann 
28445cb8eaa2SLukas Wunner 	vga_switcheroo_lock_ddc(pdev);
28455cb8eaa2SLukas Wunner 	edid = drm_get_edid(connector, adapter);
28465cb8eaa2SLukas Wunner 	vga_switcheroo_unlock_ddc(pdev);
28475cb8eaa2SLukas Wunner 
28485cb8eaa2SLukas Wunner 	return edid;
28495cb8eaa2SLukas Wunner }
28505cb8eaa2SLukas Wunner EXPORT_SYMBOL(drm_get_edid_switcheroo);
28515cb8eaa2SLukas Wunner 
28525cb8eaa2SLukas Wunner /**
28536c46f644SJani Nikula  * drm_edid_read_switcheroo - get EDID data for a vga_switcheroo output
28546c46f644SJani Nikula  * @connector: connector we're probing
28556c46f644SJani Nikula  * @adapter: I2C adapter to use for DDC
28566c46f644SJani Nikula  *
28576c46f644SJani Nikula  * Wrapper around drm_edid_read_ddc() for laptops with dual GPUs using one set
28586c46f644SJani Nikula  * of outputs. The wrapper adds the requisite vga_switcheroo calls to
28596c46f644SJani Nikula  * temporarily switch DDC to the GPU which is retrieving EDID.
28606c46f644SJani Nikula  *
28616c46f644SJani Nikula  * Return: Pointer to valid EDID or %NULL if we couldn't find any.
28626c46f644SJani Nikula  */
drm_edid_read_switcheroo(struct drm_connector * connector,struct i2c_adapter * adapter)28636c46f644SJani Nikula const struct drm_edid *drm_edid_read_switcheroo(struct drm_connector *connector,
28646c46f644SJani Nikula 						struct i2c_adapter *adapter)
28656c46f644SJani Nikula {
28666c46f644SJani Nikula 	struct drm_device *dev = connector->dev;
28676c46f644SJani Nikula 	struct pci_dev *pdev = to_pci_dev(dev->dev);
28686c46f644SJani Nikula 	const struct drm_edid *drm_edid;
28696c46f644SJani Nikula 
28706c46f644SJani Nikula 	if (drm_WARN_ON_ONCE(dev, !dev_is_pci(dev->dev)))
28716c46f644SJani Nikula 		return NULL;
28726c46f644SJani Nikula 
28736c46f644SJani Nikula 	vga_switcheroo_lock_ddc(pdev);
28746c46f644SJani Nikula 	drm_edid = drm_edid_read_ddc(connector, adapter);
28756c46f644SJani Nikula 	vga_switcheroo_unlock_ddc(pdev);
28766c46f644SJani Nikula 
28776c46f644SJani Nikula 	return drm_edid;
28786c46f644SJani Nikula }
28796c46f644SJani Nikula EXPORT_SYMBOL(drm_edid_read_switcheroo);
28806c46f644SJani Nikula 
28816c46f644SJani Nikula /**
288251f8da59SJani Nikula  * drm_edid_duplicate - duplicate an EDID and the extensions
288351f8da59SJani Nikula  * @edid: EDID to duplicate
288451f8da59SJani Nikula  *
2885db6cf833SThierry Reding  * Return: Pointer to duplicated EDID or NULL on allocation failure.
288651f8da59SJani Nikula  */
drm_edid_duplicate(const struct edid * edid)288751f8da59SJani Nikula struct edid *drm_edid_duplicate(const struct edid *edid)
288851f8da59SJani Nikula {
2889d60d2bccSJani Nikula 	if (!edid)
2890d60d2bccSJani Nikula 		return NULL;
2891d60d2bccSJani Nikula 
2892f1e4c916SJani Nikula 	return kmemdup(edid, edid_size(edid), GFP_KERNEL);
289351f8da59SJani Nikula }
289451f8da59SJani Nikula EXPORT_SYMBOL(drm_edid_duplicate);
289551f8da59SJani Nikula 
289661e57a8dSAdam Jackson /*** EDID parsing ***/
289761e57a8dSAdam Jackson 
2898f453ba04SDave Airlie /**
2899f453ba04SDave Airlie  * edid_get_quirks - return quirk flags for a given EDID
2900e42192b4SJani Nikula  * @drm_edid: EDID to process
2901f453ba04SDave Airlie  *
2902f453ba04SDave Airlie  * This tells subsequent routines what fixes they need to apply.
2903f453ba04SDave Airlie  */
edid_get_quirks(const struct drm_edid * drm_edid)2904e42192b4SJani Nikula static u32 edid_get_quirks(const struct drm_edid *drm_edid)
2905f453ba04SDave Airlie {
2906e42192b4SJani Nikula 	u32 panel_id = edid_extract_panel_id(drm_edid->edid);
290723c4cfbdSJani Nikula 	const struct edid_quirk *quirk;
2908f453ba04SDave Airlie 	int i;
2909f453ba04SDave Airlie 
2910f453ba04SDave Airlie 	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
2911f453ba04SDave Airlie 		quirk = &edid_quirk_list[i];
2912e8de4d55SDouglas Anderson 		if (quirk->panel_id == panel_id)
2913f453ba04SDave Airlie 			return quirk->quirks;
2914f453ba04SDave Airlie 	}
2915f453ba04SDave Airlie 
2916f453ba04SDave Airlie 	return 0;
2917f453ba04SDave Airlie }
2918f453ba04SDave Airlie 
2919f453ba04SDave Airlie #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
2920339d202cSAlex Deucher #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
2921f453ba04SDave Airlie 
292217edb8e1SJani Nikula /*
292317edb8e1SJani Nikula  * Walk the mode list for connector, clearing the preferred status on existing
292417edb8e1SJani Nikula  * modes and setting it anew for the right mode ala quirks.
2925f453ba04SDave Airlie  */
edid_fixup_preferred(struct drm_connector * connector)29264959b693SJani Nikula static void edid_fixup_preferred(struct drm_connector *connector)
2927f453ba04SDave Airlie {
29284959b693SJani Nikula 	const struct drm_display_info *info = &connector->display_info;
2929f453ba04SDave Airlie 	struct drm_display_mode *t, *cur_mode, *preferred_mode;
2930f890607bSDave Airlie 	int target_refresh = 0;
2931339d202cSAlex Deucher 	int cur_vrefresh, preferred_vrefresh;
2932f453ba04SDave Airlie 
2933f453ba04SDave Airlie 	if (list_empty(&connector->probed_modes))
2934f453ba04SDave Airlie 		return;
2935f453ba04SDave Airlie 
29364959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_PREFER_LARGE_60)
2937f453ba04SDave Airlie 		target_refresh = 60;
29384959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_PREFER_LARGE_75)
2939f453ba04SDave Airlie 		target_refresh = 75;
2940f453ba04SDave Airlie 
2941f453ba04SDave Airlie 	preferred_mode = list_first_entry(&connector->probed_modes,
2942f453ba04SDave Airlie 					  struct drm_display_mode, head);
2943f453ba04SDave Airlie 
2944f453ba04SDave Airlie 	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
2945f453ba04SDave Airlie 		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
2946f453ba04SDave Airlie 
2947f453ba04SDave Airlie 		if (cur_mode == preferred_mode)
2948f453ba04SDave Airlie 			continue;
2949f453ba04SDave Airlie 
2950f453ba04SDave Airlie 		/* Largest mode is preferred */
2951f453ba04SDave Airlie 		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
2952f453ba04SDave Airlie 			preferred_mode = cur_mode;
2953f453ba04SDave Airlie 
29540425662fSVille Syrjälä 		cur_vrefresh = drm_mode_vrefresh(cur_mode);
29550425662fSVille Syrjälä 		preferred_vrefresh = drm_mode_vrefresh(preferred_mode);
2956f453ba04SDave Airlie 		/* At a given size, try to get closest to target refresh */
2957f453ba04SDave Airlie 		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
2958339d202cSAlex Deucher 		    MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) <
2959339d202cSAlex Deucher 		    MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {
2960f453ba04SDave Airlie 			preferred_mode = cur_mode;
2961f453ba04SDave Airlie 		}
2962f453ba04SDave Airlie 	}
2963f453ba04SDave Airlie 
2964f453ba04SDave Airlie 	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
2965f453ba04SDave Airlie }
2966f453ba04SDave Airlie 
2967f6e252baSAdam Jackson static bool
mode_is_rb(const struct drm_display_mode * mode)2968f6e252baSAdam Jackson mode_is_rb(const struct drm_display_mode *mode)
2969f6e252baSAdam Jackson {
2970f6e252baSAdam Jackson 	return (mode->htotal - mode->hdisplay == 160) &&
2971f6e252baSAdam Jackson 	       (mode->hsync_end - mode->hdisplay == 80) &&
2972f6e252baSAdam Jackson 	       (mode->hsync_end - mode->hsync_start == 32) &&
2973f6e252baSAdam Jackson 	       (mode->vsync_start - mode->vdisplay == 3);
2974f6e252baSAdam Jackson }
2975f6e252baSAdam Jackson 
297633c7531dSAdam Jackson /*
297733c7531dSAdam Jackson  * drm_mode_find_dmt - Create a copy of a mode if present in DMT
297833c7531dSAdam Jackson  * @dev: Device to duplicate against
297933c7531dSAdam Jackson  * @hsize: Mode width
298033c7531dSAdam Jackson  * @vsize: Mode height
298133c7531dSAdam Jackson  * @fresh: Mode refresh rate
2982f6e252baSAdam Jackson  * @rb: Mode reduced-blanking-ness
298333c7531dSAdam Jackson  *
298433c7531dSAdam Jackson  * Walk the DMT mode list looking for a match for the given parameters.
2985db6cf833SThierry Reding  *
2986db6cf833SThierry Reding  * Return: A newly allocated copy of the mode, or NULL if not found.
298733c7531dSAdam Jackson  */
drm_mode_find_dmt(struct drm_device * dev,int hsize,int vsize,int fresh,bool rb)29881d42bbc8SDave Airlie struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
2989f6e252baSAdam Jackson 					   int hsize, int vsize, int fresh,
2990f6e252baSAdam Jackson 					   bool rb)
2991559ee21dSZhao Yakui {
299207a5e632SAdam Jackson 	int i;
2993559ee21dSZhao Yakui 
2994a6b21831SThierry Reding 	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
2995b1f559ecSChris Wilson 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
2996948de842SSuraj Upadhyay 
2997f8b46a05SAdam Jackson 		if (hsize != ptr->hdisplay)
2998f8b46a05SAdam Jackson 			continue;
2999f8b46a05SAdam Jackson 		if (vsize != ptr->vdisplay)
3000f8b46a05SAdam Jackson 			continue;
3001f8b46a05SAdam Jackson 		if (fresh != drm_mode_vrefresh(ptr))
3002f8b46a05SAdam Jackson 			continue;
3003f6e252baSAdam Jackson 		if (rb != mode_is_rb(ptr))
3004f6e252baSAdam Jackson 			continue;
3005f8b46a05SAdam Jackson 
3006f8b46a05SAdam Jackson 		return drm_mode_duplicate(dev, ptr);
3007559ee21dSZhao Yakui 	}
3008f8b46a05SAdam Jackson 
3009f8b46a05SAdam Jackson 	return NULL;
3010559ee21dSZhao Yakui }
30111d42bbc8SDave Airlie EXPORT_SYMBOL(drm_mode_find_dmt);
301223425caeSAdam Jackson 
is_display_descriptor(const struct detailed_timing * descriptor,u8 type)3013e379814bSJani Nikula static bool is_display_descriptor(const struct detailed_timing *descriptor, u8 type)
3014a7a131acSVille Syrjälä {
3015e379814bSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
3016e379814bSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.pad1) != 2);
3017e379814bSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.type) != 3);
3018e379814bSJani Nikula 
3019e379814bSJani Nikula 	return descriptor->pixel_clock == 0 &&
3020e379814bSJani Nikula 		descriptor->data.other_data.pad1 == 0 &&
3021e379814bSJani Nikula 		descriptor->data.other_data.type == type;
3022a7a131acSVille Syrjälä }
3023a7a131acSVille Syrjälä 
is_detailed_timing_descriptor(const struct detailed_timing * descriptor)3024a9b1f15fSJani Nikula static bool is_detailed_timing_descriptor(const struct detailed_timing *descriptor)
3025f447dd1fSVille Syrjälä {
3026a9b1f15fSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
3027a9b1f15fSJani Nikula 
3028a9b1f15fSJani Nikula 	return descriptor->pixel_clock != 0;
3029f447dd1fSVille Syrjälä }
3030f447dd1fSVille Syrjälä 
30314194442dSJani Nikula typedef void detailed_cb(const struct detailed_timing *timing, void *closure);
3032d1ff6409SAdam Jackson 
3033d1ff6409SAdam Jackson static void
cea_for_each_detailed_block(const u8 * ext,detailed_cb * cb,void * closure)3034eed628f1SJani Nikula cea_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
30354d76a221SAdam Jackson {
30367304b981SVille Syrjälä 	int i, n;
30374966b2a9SChristian Schmidt 	u8 d = ext[0x02];
3038eed628f1SJani Nikula 	const u8 *det_base = ext + d;
30394d76a221SAdam Jackson 
30407304b981SVille Syrjälä 	if (d < 4 || d > 127)
30417304b981SVille Syrjälä 		return;
30427304b981SVille Syrjälä 
30434966b2a9SChristian Schmidt 	n = (127 - d) / 18;
30444d76a221SAdam Jackson 	for (i = 0; i < n; i++)
3045eed628f1SJani Nikula 		cb((const struct detailed_timing *)(det_base + 18 * i), closure);
30464d76a221SAdam Jackson }
30474d76a221SAdam Jackson 
30484d76a221SAdam Jackson static void
vtb_for_each_detailed_block(const u8 * ext,detailed_cb * cb,void * closure)3049eed628f1SJani Nikula vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
3050cbba98f8SAdam Jackson {
3051cbba98f8SAdam Jackson 	unsigned int i, n = min((int)ext[0x02], 6);
3052eed628f1SJani Nikula 	const u8 *det_base = ext + 5;
3053cbba98f8SAdam Jackson 
3054cbba98f8SAdam Jackson 	if (ext[0x01] != 1)
3055cbba98f8SAdam Jackson 		return; /* unknown version */
3056cbba98f8SAdam Jackson 
3057cbba98f8SAdam Jackson 	for (i = 0; i < n; i++)
3058eed628f1SJani Nikula 		cb((const struct detailed_timing *)(det_base + 18 * i), closure);
3059cbba98f8SAdam Jackson }
3060cbba98f8SAdam Jackson 
drm_for_each_detailed_block(const struct drm_edid * drm_edid,detailed_cb * cb,void * closure)306145aa2336SJani Nikula static void drm_for_each_detailed_block(const struct drm_edid *drm_edid,
306245aa2336SJani Nikula 					detailed_cb *cb, void *closure)
3063d1ff6409SAdam Jackson {
3064ab1747ccSJani Nikula 	struct drm_edid_iter edid_iter;
3065ab1747ccSJani Nikula 	const u8 *ext;
3066d1ff6409SAdam Jackson 	int i;
3067d1ff6409SAdam Jackson 
306845aa2336SJani Nikula 	if (!drm_edid)
3069d1ff6409SAdam Jackson 		return;
3070d1ff6409SAdam Jackson 
3071d1ff6409SAdam Jackson 	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
307245aa2336SJani Nikula 		cb(&drm_edid->edid->detailed_timings[i], closure);
3073d1ff6409SAdam Jackson 
3074bbded689SJani Nikula 	drm_edid_iter_begin(drm_edid, &edid_iter);
3075ab1747ccSJani Nikula 	drm_edid_iter_for_each(ext, &edid_iter) {
30764d76a221SAdam Jackson 		switch (*ext) {
30774d76a221SAdam Jackson 		case CEA_EXT:
30784d76a221SAdam Jackson 			cea_for_each_detailed_block(ext, cb, closure);
30794d76a221SAdam Jackson 			break;
3080cbba98f8SAdam Jackson 		case VTB_EXT:
3081cbba98f8SAdam Jackson 			vtb_for_each_detailed_block(ext, cb, closure);
3082cbba98f8SAdam Jackson 			break;
30834d76a221SAdam Jackson 		default:
30844d76a221SAdam Jackson 			break;
30854d76a221SAdam Jackson 		}
30864d76a221SAdam Jackson 	}
3087ab1747ccSJani Nikula 	drm_edid_iter_end(&edid_iter);
3088d1ff6409SAdam Jackson }
3089d1ff6409SAdam Jackson 
3090d1ff6409SAdam Jackson static void
is_rb(const struct detailed_timing * descriptor,void * data)30914194442dSJani Nikula is_rb(const struct detailed_timing *descriptor, void *data)
3092d1ff6409SAdam Jackson {
309390fd588fSJani Nikula 	bool *res = data;
3094a7a131acSVille Syrjälä 
309590fd588fSJani Nikula 	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
3096a7a131acSVille Syrjälä 		return;
3097a7a131acSVille Syrjälä 
309890fd588fSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
309990fd588fSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.cvt.flags) != 15);
310090fd588fSJani Nikula 
310190fd588fSJani Nikula 	if (descriptor->data.other_data.data.range.flags == DRM_EDID_CVT_SUPPORT_FLAG &&
3102afd4429eSVille Syrjälä 	    descriptor->data.other_data.data.range.formula.cvt.flags & DRM_EDID_CVT_FLAGS_REDUCED_BLANKING)
310390fd588fSJani Nikula 		*res = true;
3104d1ff6409SAdam Jackson }
3105d1ff6409SAdam Jackson 
3106d1ff6409SAdam Jackson /* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
3107d1ff6409SAdam Jackson static bool
drm_monitor_supports_rb(const struct drm_edid * drm_edid)3108874d98eeSJani Nikula drm_monitor_supports_rb(const struct drm_edid *drm_edid)
3109d1ff6409SAdam Jackson {
3110874d98eeSJani Nikula 	if (drm_edid->edid->revision >= 4) {
3111b196a498SDaniel Vetter 		bool ret = false;
3112948de842SSuraj Upadhyay 
311345aa2336SJani Nikula 		drm_for_each_detailed_block(drm_edid, is_rb, &ret);
3114d1ff6409SAdam Jackson 		return ret;
3115d1ff6409SAdam Jackson 	}
3116d1ff6409SAdam Jackson 
3117874d98eeSJani Nikula 	return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
3118d1ff6409SAdam Jackson }
3119d1ff6409SAdam Jackson 
31207a374350SAdam Jackson static void
find_gtf2(const struct detailed_timing * descriptor,void * data)31214194442dSJani Nikula find_gtf2(const struct detailed_timing *descriptor, void *data)
31227a374350SAdam Jackson {
31234194442dSJani Nikula 	const struct detailed_timing **res = data;
3124a7a131acSVille Syrjälä 
3125c8a4bebaSJani Nikula 	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
3126a7a131acSVille Syrjälä 		return;
3127a7a131acSVille Syrjälä 
3128c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
3129c8a4bebaSJani Nikula 
3130afd4429eSVille Syrjälä 	if (descriptor->data.other_data.data.range.flags == DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG)
3131c8a4bebaSJani Nikula 		*res = descriptor;
31327a374350SAdam Jackson }
31337a374350SAdam Jackson 
31347a374350SAdam Jackson /* Secondary GTF curve kicks in above some break frequency */
31357a374350SAdam Jackson static int
drm_gtf2_hbreak(const struct drm_edid * drm_edid)313667d87facSJani Nikula drm_gtf2_hbreak(const struct drm_edid *drm_edid)
31377a374350SAdam Jackson {
31384194442dSJani Nikula 	const struct detailed_timing *descriptor = NULL;
3139948de842SSuraj Upadhyay 
314045aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
3141c8a4bebaSJani Nikula 
3142c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12);
3143c8a4bebaSJani Nikula 
3144c8a4bebaSJani Nikula 	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.hfreq_start_khz * 2 : 0;
31457a374350SAdam Jackson }
31467a374350SAdam Jackson 
31477a374350SAdam Jackson static int
drm_gtf2_2c(const struct drm_edid * drm_edid)314867d87facSJani Nikula drm_gtf2_2c(const struct drm_edid *drm_edid)
31497a374350SAdam Jackson {
31504194442dSJani Nikula 	const struct detailed_timing *descriptor = NULL;
3151948de842SSuraj Upadhyay 
315245aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
3153c8a4bebaSJani Nikula 
3154c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13);
3155c8a4bebaSJani Nikula 
3156c8a4bebaSJani Nikula 	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.c : 0;
31577a374350SAdam Jackson }
31587a374350SAdam Jackson 
31597a374350SAdam Jackson static int
drm_gtf2_m(const struct drm_edid * drm_edid)316067d87facSJani Nikula drm_gtf2_m(const struct drm_edid *drm_edid)
31617a374350SAdam Jackson {
31624194442dSJani Nikula 	const struct detailed_timing *descriptor = NULL;
3163948de842SSuraj Upadhyay 
316445aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
3165c8a4bebaSJani Nikula 
3166c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14);
3167c8a4bebaSJani Nikula 
3168c8a4bebaSJani Nikula 	return descriptor ? le16_to_cpu(descriptor->data.other_data.data.range.formula.gtf2.m) : 0;
31697a374350SAdam Jackson }
31707a374350SAdam Jackson 
31717a374350SAdam Jackson static int
drm_gtf2_k(const struct drm_edid * drm_edid)317267d87facSJani Nikula drm_gtf2_k(const struct drm_edid *drm_edid)
31737a374350SAdam Jackson {
31744194442dSJani Nikula 	const struct detailed_timing *descriptor = NULL;
3175948de842SSuraj Upadhyay 
317645aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
3177c8a4bebaSJani Nikula 
3178c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16);
3179c8a4bebaSJani Nikula 
3180c8a4bebaSJani Nikula 	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.k : 0;
31817a374350SAdam Jackson }
31827a374350SAdam Jackson 
31837a374350SAdam Jackson static int
drm_gtf2_2j(const struct drm_edid * drm_edid)318467d87facSJani Nikula drm_gtf2_2j(const struct drm_edid *drm_edid)
31857a374350SAdam Jackson {
31864194442dSJani Nikula 	const struct detailed_timing *descriptor = NULL;
3187948de842SSuraj Upadhyay 
318845aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
3189c8a4bebaSJani Nikula 
3190c8a4bebaSJani Nikula 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17);
3191c8a4bebaSJani Nikula 
3192c8a4bebaSJani Nikula 	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0;
31937a374350SAdam Jackson }
31947a374350SAdam Jackson 
3195bf72b5efSVille Syrjälä static void
get_timing_level(const struct detailed_timing * descriptor,void * data)3196bf72b5efSVille Syrjälä get_timing_level(const struct detailed_timing *descriptor, void *data)
3197bf72b5efSVille Syrjälä {
3198bf72b5efSVille Syrjälä 	int *res = data;
3199bf72b5efSVille Syrjälä 
3200bf72b5efSVille Syrjälä 	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
3201bf72b5efSVille Syrjälä 		return;
3202bf72b5efSVille Syrjälä 
3203bf72b5efSVille Syrjälä 	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
3204bf72b5efSVille Syrjälä 
3205bf72b5efSVille Syrjälä 	switch (descriptor->data.other_data.data.range.flags) {
3206bf72b5efSVille Syrjälä 	case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG:
3207bf72b5efSVille Syrjälä 		*res = LEVEL_GTF;
3208bf72b5efSVille Syrjälä 		break;
3209bf72b5efSVille Syrjälä 	case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG:
3210bf72b5efSVille Syrjälä 		*res = LEVEL_GTF2;
3211bf72b5efSVille Syrjälä 		break;
3212bf72b5efSVille Syrjälä 	case DRM_EDID_CVT_SUPPORT_FLAG:
3213bf72b5efSVille Syrjälä 		*res = LEVEL_CVT;
3214bf72b5efSVille Syrjälä 		break;
3215bf72b5efSVille Syrjälä 	default:
3216bf72b5efSVille Syrjälä 		break;
3217bf72b5efSVille Syrjälä 	}
3218bf72b5efSVille Syrjälä }
3219bf72b5efSVille Syrjälä 
322017edb8e1SJani Nikula /* Get standard timing level (CVT/GTF/DMT). */
standard_timing_level(const struct drm_edid * drm_edid)322167d87facSJani Nikula static int standard_timing_level(const struct drm_edid *drm_edid)
32227a374350SAdam Jackson {
322367d87facSJani Nikula 	const struct edid *edid = drm_edid->edid;
322467d87facSJani Nikula 
3225bf72b5efSVille Syrjälä 	if (edid->revision >= 4) {
3226bf72b5efSVille Syrjälä 		/*
3227bf72b5efSVille Syrjälä 		 * If the range descriptor doesn't
3228bf72b5efSVille Syrjälä 		 * indicate otherwise default to CVT
3229bf72b5efSVille Syrjälä 		 */
3230bf72b5efSVille Syrjälä 		int ret = LEVEL_CVT;
3231bf72b5efSVille Syrjälä 
3232bf72b5efSVille Syrjälä 		drm_for_each_detailed_block(drm_edid, get_timing_level, &ret);
3233bf72b5efSVille Syrjälä 
3234bf72b5efSVille Syrjälä 		return ret;
3235bf72b5efSVille Syrjälä 	} else if (edid->revision >= 3 && drm_gtf2_hbreak(drm_edid)) {
32367a374350SAdam Jackson 		return LEVEL_GTF2;
3237bf72b5efSVille Syrjälä 	} else if (edid->revision >= 2) {
32387a374350SAdam Jackson 		return LEVEL_GTF;
3239bf72b5efSVille Syrjälä 	} else {
32407a374350SAdam Jackson 		return LEVEL_DMT;
32417a374350SAdam Jackson 	}
3242bf72b5efSVille Syrjälä }
32437a374350SAdam Jackson 
324423425caeSAdam Jackson /*
324523425caeSAdam Jackson  * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
324623425caeSAdam Jackson  * monitors fill with ascii space (0x20) instead.
324723425caeSAdam Jackson  */
324823425caeSAdam Jackson static int
bad_std_timing(u8 a,u8 b)324923425caeSAdam Jackson bad_std_timing(u8 a, u8 b)
325023425caeSAdam Jackson {
325123425caeSAdam Jackson 	return (a == 0x00 && b == 0x00) ||
325223425caeSAdam Jackson 	       (a == 0x01 && b == 0x01) ||
325323425caeSAdam Jackson 	       (a == 0x20 && b == 0x20);
325423425caeSAdam Jackson }
325523425caeSAdam Jackson 
drm_mode_hsync(const struct drm_display_mode * mode)325658911c24SVille Syrjälä static int drm_mode_hsync(const struct drm_display_mode *mode)
325758911c24SVille Syrjälä {
325858911c24SVille Syrjälä 	if (mode->htotal <= 0)
325958911c24SVille Syrjälä 		return 0;
326058911c24SVille Syrjälä 
326158911c24SVille Syrjälä 	return DIV_ROUND_CLOSEST(mode->clock, mode->htotal);
326258911c24SVille Syrjälä }
326358911c24SVille Syrjälä 
326486101bb7SVille Syrjälä static struct drm_display_mode *
drm_gtf2_mode(struct drm_device * dev,const struct drm_edid * drm_edid,int hsize,int vsize,int vrefresh_rate)326586101bb7SVille Syrjälä drm_gtf2_mode(struct drm_device *dev,
326686101bb7SVille Syrjälä 	      const struct drm_edid *drm_edid,
326786101bb7SVille Syrjälä 	      int hsize, int vsize, int vrefresh_rate)
326886101bb7SVille Syrjälä {
326986101bb7SVille Syrjälä 	struct drm_display_mode *mode;
327086101bb7SVille Syrjälä 
327186101bb7SVille Syrjälä 	/*
327286101bb7SVille Syrjälä 	 * This is potentially wrong if there's ever a monitor with
327386101bb7SVille Syrjälä 	 * more than one ranges section, each claiming a different
327486101bb7SVille Syrjälä 	 * secondary GTF curve.  Please don't do that.
327586101bb7SVille Syrjälä 	 */
327686101bb7SVille Syrjälä 	mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
327786101bb7SVille Syrjälä 	if (!mode)
327886101bb7SVille Syrjälä 		return NULL;
327986101bb7SVille Syrjälä 
328086101bb7SVille Syrjälä 	if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) {
328186101bb7SVille Syrjälä 		drm_mode_destroy(dev, mode);
328286101bb7SVille Syrjälä 		mode = drm_gtf_mode_complex(dev, hsize, vsize,
328386101bb7SVille Syrjälä 					    vrefresh_rate, 0, 0,
328486101bb7SVille Syrjälä 					    drm_gtf2_m(drm_edid),
328586101bb7SVille Syrjälä 					    drm_gtf2_2c(drm_edid),
328686101bb7SVille Syrjälä 					    drm_gtf2_k(drm_edid),
328786101bb7SVille Syrjälä 					    drm_gtf2_2j(drm_edid));
328886101bb7SVille Syrjälä 	}
328986101bb7SVille Syrjälä 
329086101bb7SVille Syrjälä 	return mode;
329186101bb7SVille Syrjälä }
329286101bb7SVille Syrjälä 
329317edb8e1SJani Nikula /*
3294f453ba04SDave Airlie  * Take the standard timing params (in this case width, aspect, and refresh)
32955c61259eSZhao Yakui  * and convert them into a real mode using CVT/GTF/DMT.
3296f453ba04SDave Airlie  */
drm_mode_std(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct std_timing * t)329767d87facSJani Nikula static struct drm_display_mode *drm_mode_std(struct drm_connector *connector,
329867d87facSJani Nikula 					     const struct drm_edid *drm_edid,
3299fcfb2ea1SJani Nikula 					     const struct std_timing *t)
3300f453ba04SDave Airlie {
33017ca6adb3SAdam Jackson 	struct drm_device *dev = connector->dev;
33027ca6adb3SAdam Jackson 	struct drm_display_mode *m, *mode = NULL;
33035c61259eSZhao Yakui 	int hsize, vsize;
33045c61259eSZhao Yakui 	int vrefresh_rate;
33050454beabSMichel Dänzer 	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
33060454beabSMichel Dänzer 		>> EDID_TIMING_ASPECT_SHIFT;
33075c61259eSZhao Yakui 	unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
33085c61259eSZhao Yakui 		>> EDID_TIMING_VFREQ_SHIFT;
330967d87facSJani Nikula 	int timing_level = standard_timing_level(drm_edid);
3310f453ba04SDave Airlie 
331123425caeSAdam Jackson 	if (bad_std_timing(t->hsize, t->vfreq_aspect))
331223425caeSAdam Jackson 		return NULL;
331323425caeSAdam Jackson 
33145c61259eSZhao Yakui 	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
33155c61259eSZhao Yakui 	hsize = t->hsize * 8 + 248;
33165c61259eSZhao Yakui 	/* vrefresh_rate = vfreq + 60 */
33175c61259eSZhao Yakui 	vrefresh_rate = vfreq + 60;
33185c61259eSZhao Yakui 	/* the vdisplay is calculated based on the aspect ratio */
3319f066a17dSAdam Jackson 	if (aspect_ratio == 0) {
332067d87facSJani Nikula 		if (drm_edid->edid->revision < 3)
3321f066a17dSAdam Jackson 			vsize = hsize;
3322f066a17dSAdam Jackson 		else
3323f453ba04SDave Airlie 			vsize = (hsize * 10) / 16;
3324f066a17dSAdam Jackson 	} else if (aspect_ratio == 1)
3325f453ba04SDave Airlie 		vsize = (hsize * 3) / 4;
33260454beabSMichel Dänzer 	else if (aspect_ratio == 2)
3327f453ba04SDave Airlie 		vsize = (hsize * 4) / 5;
3328f453ba04SDave Airlie 	else
3329f453ba04SDave Airlie 		vsize = (hsize * 9) / 16;
3330a0910c8eSAdam Jackson 
3331a0910c8eSAdam Jackson 	/* HDTV hack, part 1 */
3332a0910c8eSAdam Jackson 	if (vrefresh_rate == 60 &&
3333a0910c8eSAdam Jackson 	    ((hsize == 1360 && vsize == 765) ||
3334a0910c8eSAdam Jackson 	     (hsize == 1368 && vsize == 769))) {
3335a0910c8eSAdam Jackson 		hsize = 1366;
3336a0910c8eSAdam Jackson 		vsize = 768;
3337a0910c8eSAdam Jackson 	}
3338a0910c8eSAdam Jackson 
33397ca6adb3SAdam Jackson 	/*
33407ca6adb3SAdam Jackson 	 * If this connector already has a mode for this size and refresh
33417ca6adb3SAdam Jackson 	 * rate (because it came from detailed or CVT info), use that
33427ca6adb3SAdam Jackson 	 * instead.  This way we don't have to guess at interlace or
33437ca6adb3SAdam Jackson 	 * reduced blanking.
33447ca6adb3SAdam Jackson 	 */
3345522032daSAdam Jackson 	list_for_each_entry(m, &connector->probed_modes, head)
33467ca6adb3SAdam Jackson 		if (m->hdisplay == hsize && m->vdisplay == vsize &&
33477ca6adb3SAdam Jackson 		    drm_mode_vrefresh(m) == vrefresh_rate)
33487ca6adb3SAdam Jackson 			return NULL;
33497ca6adb3SAdam Jackson 
3350a0910c8eSAdam Jackson 	/* HDTV hack, part 2 */
3351a0910c8eSAdam Jackson 	if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
3352a0910c8eSAdam Jackson 		mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
3353d50ba256SDave Airlie 				    false);
3354a5ef6567SJoe Moriarty 		if (!mode)
3355a5ef6567SJoe Moriarty 			return NULL;
3356559ee21dSZhao Yakui 		mode->hdisplay = 1366;
3357a4967de6SAdam Jackson 		mode->hsync_start = mode->hsync_start - 1;
3358a4967de6SAdam Jackson 		mode->hsync_end = mode->hsync_end - 1;
3359559ee21dSZhao Yakui 		return mode;
3360559ee21dSZhao Yakui 	}
3361a0910c8eSAdam Jackson 
3362559ee21dSZhao Yakui 	/* check whether it can be found in default mode table */
3363874d98eeSJani Nikula 	if (drm_monitor_supports_rb(drm_edid)) {
3364f6e252baSAdam Jackson 		mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
3365f6e252baSAdam Jackson 					 true);
3366f6e252baSAdam Jackson 		if (mode)
3367f6e252baSAdam Jackson 			return mode;
3368f6e252baSAdam Jackson 	}
3369f6e252baSAdam Jackson 	mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false);
3370559ee21dSZhao Yakui 	if (mode)
3371559ee21dSZhao Yakui 		return mode;
3372559ee21dSZhao Yakui 
3373f6e252baSAdam Jackson 	/* okay, generate it */
33745c61259eSZhao Yakui 	switch (timing_level) {
33755c61259eSZhao Yakui 	case LEVEL_DMT:
33765c61259eSZhao Yakui 		break;
33775c61259eSZhao Yakui 	case LEVEL_GTF:
33785c61259eSZhao Yakui 		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
33795c61259eSZhao Yakui 		break;
33807a374350SAdam Jackson 	case LEVEL_GTF2:
338186101bb7SVille Syrjälä 		mode = drm_gtf2_mode(dev, drm_edid, hsize, vsize, vrefresh_rate);
33827a374350SAdam Jackson 		break;
33835c61259eSZhao Yakui 	case LEVEL_CVT:
3384d50ba256SDave Airlie 		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
3385d50ba256SDave Airlie 				    false);
33865c61259eSZhao Yakui 		break;
33875c61259eSZhao Yakui 	}
3388f453ba04SDave Airlie 	return mode;
3389f453ba04SDave Airlie }
3390f453ba04SDave Airlie 
3391b58db2c6SAdam Jackson /*
3392b58db2c6SAdam Jackson  * EDID is delightfully ambiguous about how interlaced modes are to be
3393b58db2c6SAdam Jackson  * encoded.  Our internal representation is of frame height, but some
3394b58db2c6SAdam Jackson  * HDTV detailed timings are encoded as field height.
3395b58db2c6SAdam Jackson  *
3396b58db2c6SAdam Jackson  * The format list here is from CEA, in frame size.  Technically we
3397b58db2c6SAdam Jackson  * should be checking refresh rate too.  Whatever.
3398b58db2c6SAdam Jackson  */
3399b58db2c6SAdam Jackson static void
drm_mode_do_interlace_quirk(struct drm_display_mode * mode,const struct detailed_pixel_timing * pt)3400b58db2c6SAdam Jackson drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
3401fcfb2ea1SJani Nikula 			    const struct detailed_pixel_timing *pt)
3402b58db2c6SAdam Jackson {
3403b58db2c6SAdam Jackson 	int i;
3404b58db2c6SAdam Jackson 	static const struct {
3405b58db2c6SAdam Jackson 		int w, h;
3406b58db2c6SAdam Jackson 	} cea_interlaced[] = {
3407b58db2c6SAdam Jackson 		{ 1920, 1080 },
3408b58db2c6SAdam Jackson 		{  720,  480 },
3409b58db2c6SAdam Jackson 		{ 1440,  480 },
3410b58db2c6SAdam Jackson 		{ 2880,  480 },
3411b58db2c6SAdam Jackson 		{  720,  576 },
3412b58db2c6SAdam Jackson 		{ 1440,  576 },
3413b58db2c6SAdam Jackson 		{ 2880,  576 },
3414b58db2c6SAdam Jackson 	};
3415b58db2c6SAdam Jackson 
3416b58db2c6SAdam Jackson 	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
3417b58db2c6SAdam Jackson 		return;
3418b58db2c6SAdam Jackson 
34193c581411SKulikov Vasiliy 	for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
3420b58db2c6SAdam Jackson 		if ((mode->hdisplay == cea_interlaced[i].w) &&
3421b58db2c6SAdam Jackson 		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
3422b58db2c6SAdam Jackson 			mode->vdisplay *= 2;
3423b58db2c6SAdam Jackson 			mode->vsync_start *= 2;
3424b58db2c6SAdam Jackson 			mode->vsync_end *= 2;
3425b58db2c6SAdam Jackson 			mode->vtotal *= 2;
3426b58db2c6SAdam Jackson 			mode->vtotal |= 1;
3427b58db2c6SAdam Jackson 		}
3428b58db2c6SAdam Jackson 	}
3429b58db2c6SAdam Jackson 
3430b58db2c6SAdam Jackson 	mode->flags |= DRM_MODE_FLAG_INTERLACE;
3431b58db2c6SAdam Jackson }
3432b58db2c6SAdam Jackson 
343317edb8e1SJani Nikula /*
343417edb8e1SJani Nikula  * Create a new mode from an EDID detailed timing section. An EDID detailed
343517edb8e1SJani Nikula  * timing block contains enough info for us to create and return a new struct
343617edb8e1SJani Nikula  * drm_display_mode.
3437f453ba04SDave Airlie  */
drm_mode_detailed(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct detailed_timing * timing)3438e1e7bc48SJani Nikula static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector,
3439f0d080ffSJani Nikula 						  const struct drm_edid *drm_edid,
34404959b693SJani Nikula 						  const struct detailed_timing *timing)
3441f453ba04SDave Airlie {
34424959b693SJani Nikula 	const struct drm_display_info *info = &connector->display_info;
3443e1e7bc48SJani Nikula 	struct drm_device *dev = connector->dev;
3444f453ba04SDave Airlie 	struct drm_display_mode *mode;
3445fcfb2ea1SJani Nikula 	const struct detailed_pixel_timing *pt = &timing->data.pixel_data;
34460454beabSMichel Dänzer 	unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
34470454beabSMichel Dänzer 	unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
34480454beabSMichel Dänzer 	unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
34490454beabSMichel Dänzer 	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;
3450e14cbee4SMichel Dänzer 	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;
3451e14cbee4SMichel Dänzer 	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
345216dad1d7STorsten Duwe 	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;
3453e14cbee4SMichel Dänzer 	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
3454f453ba04SDave Airlie 
3455fc438966SAdam Jackson 	/* ignore tiny modes */
34560454beabSMichel Dänzer 	if (hactive < 64 || vactive < 64)
3457fc438966SAdam Jackson 		return NULL;
3458fc438966SAdam Jackson 
34590454beabSMichel Dänzer 	if (pt->misc & DRM_EDID_PT_STEREO) {
3460e1e7bc48SJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Stereo mode not supported\n",
3461e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
3462f453ba04SDave Airlie 		return NULL;
3463f453ba04SDave Airlie 	}
346450b6f2c8SJani Nikula 	if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
346550b6f2c8SJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Composite sync not supported\n",
346650b6f2c8SJani Nikula 			    connector->base.id, connector->name);
346750b6f2c8SJani Nikula 	}
3468f453ba04SDave Airlie 
3469fcb45611SZhao Yakui 	/* it is incorrect if hsync/vsync width is zero */
3470fcb45611SZhao Yakui 	if (!hsync_pulse_width || !vsync_pulse_width) {
3471e1e7bc48SJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Incorrect Detailed timing. Wrong Hsync/Vsync pulse width\n",
3472e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
3473fcb45611SZhao Yakui 		return NULL;
3474fcb45611SZhao Yakui 	}
3475bc42aabcSAdam Jackson 
34764959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
3477bc42aabcSAdam Jackson 		mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
3478f453ba04SDave Airlie 		if (!mode)
3479f453ba04SDave Airlie 			return NULL;
3480f453ba04SDave Airlie 
3481bc42aabcSAdam Jackson 		goto set_size;
3482bc42aabcSAdam Jackson 	}
3483bc42aabcSAdam Jackson 
3484bc42aabcSAdam Jackson 	mode = drm_mode_create(dev);
3485bc42aabcSAdam Jackson 	if (!mode)
3486bc42aabcSAdam Jackson 		return NULL;
3487f453ba04SDave Airlie 
34884959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
3489faacff8eSJani Nikula 		mode->clock = 1088 * 10;
3490faacff8eSJani Nikula 	else
34910454beabSMichel Dänzer 		mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
3492f453ba04SDave Airlie 
34930454beabSMichel Dänzer 	mode->hdisplay = hactive;
34940454beabSMichel Dänzer 	mode->hsync_start = mode->hdisplay + hsync_offset;
34950454beabSMichel Dänzer 	mode->hsync_end = mode->hsync_start + hsync_pulse_width;
34960454beabSMichel Dänzer 	mode->htotal = mode->hdisplay + hblank;
3497f453ba04SDave Airlie 
34980454beabSMichel Dänzer 	mode->vdisplay = vactive;
34990454beabSMichel Dänzer 	mode->vsync_start = mode->vdisplay + vsync_offset;
35000454beabSMichel Dänzer 	mode->vsync_end = mode->vsync_start + vsync_pulse_width;
35010454beabSMichel Dänzer 	mode->vtotal = mode->vdisplay + vblank;
3502f453ba04SDave Airlie 
3503d78c4efaSVille Syrjälä 	/* Some EDIDs have bogus h/vsync_end values */
3504d78c4efaSVille Syrjälä 	if (mode->hsync_end > mode->htotal) {
3505d78c4efaSVille Syrjälä 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing hsync_end %d->%d\n",
3506d78c4efaSVille Syrjälä 			    connector->base.id, connector->name,
3507d78c4efaSVille Syrjälä 			    mode->hsync_end, mode->htotal);
3508d78c4efaSVille Syrjälä 		mode->hsync_end = mode->htotal;
3509d78c4efaSVille Syrjälä 	}
3510d78c4efaSVille Syrjälä 	if (mode->vsync_end > mode->vtotal) {
3511d78c4efaSVille Syrjälä 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing vsync_end %d->%d\n",
3512d78c4efaSVille Syrjälä 			    connector->base.id, connector->name,
3513d78c4efaSVille Syrjälä 			    mode->vsync_end, mode->vtotal);
3514d78c4efaSVille Syrjälä 		mode->vsync_end = mode->vtotal;
3515d78c4efaSVille Syrjälä 	}
35167064fef5SJesse Barnes 
3517b58db2c6SAdam Jackson 	drm_mode_do_interlace_quirk(mode, pt);
3518f453ba04SDave Airlie 
35194959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
3520faacff8eSJani Nikula 		mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
3521faacff8eSJani Nikula 	} else {
35220454beabSMichel Dänzer 		mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
35230454beabSMichel Dänzer 			DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
35240454beabSMichel Dänzer 		mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
35250454beabSMichel Dänzer 			DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
3526faacff8eSJani Nikula 	}
3527f453ba04SDave Airlie 
3528bc42aabcSAdam Jackson set_size:
3529e14cbee4SMichel Dänzer 	mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
3530e14cbee4SMichel Dänzer 	mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
3531f453ba04SDave Airlie 
35324959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_DETAILED_IN_CM) {
3533f453ba04SDave Airlie 		mode->width_mm *= 10;
3534f453ba04SDave Airlie 		mode->height_mm *= 10;
3535f453ba04SDave Airlie 	}
3536f453ba04SDave Airlie 
35374959b693SJani Nikula 	if (info->quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
3538f0d080ffSJani Nikula 		mode->width_mm = drm_edid->edid->width_cm * 10;
3539f0d080ffSJani Nikula 		mode->height_mm = drm_edid->edid->height_cm * 10;
3540f453ba04SDave Airlie 	}
3541f453ba04SDave Airlie 
3542bc42aabcSAdam Jackson 	mode->type = DRM_MODE_TYPE_DRIVER;
3543bc42aabcSAdam Jackson 	drm_mode_set_name(mode);
3544bc42aabcSAdam Jackson 
3545f453ba04SDave Airlie 	return mode;
3546f453ba04SDave Airlie }
3547f453ba04SDave Airlie 
354807a5e632SAdam Jackson static bool
mode_in_hsync_range(const struct drm_display_mode * mode,const struct edid * edid,const u8 * t)3549b1f559ecSChris Wilson mode_in_hsync_range(const struct drm_display_mode *mode,
3550c14e7241SJani Nikula 		    const struct edid *edid, const u8 *t)
3551b17e52efSAdam Jackson {
3552b17e52efSAdam Jackson 	int hsync, hmin, hmax;
355307a5e632SAdam Jackson 
3554b17e52efSAdam Jackson 	hmin = t[7];
3555b17e52efSAdam Jackson 	if (edid->revision >= 4)
3556b17e52efSAdam Jackson 	    hmin += ((t[4] & 0x04) ? 255 : 0);
3557b17e52efSAdam Jackson 	hmax = t[8];
3558b17e52efSAdam Jackson 	if (edid->revision >= 4)
3559b17e52efSAdam Jackson 	    hmax += ((t[4] & 0x08) ? 255 : 0);
356007a5e632SAdam Jackson 	hsync = drm_mode_hsync(mode);
356107a5e632SAdam Jackson 
3562b17e52efSAdam Jackson 	return (hsync <= hmax && hsync >= hmin);
3563b17e52efSAdam Jackson }
3564b17e52efSAdam Jackson 
3565b17e52efSAdam Jackson static bool
mode_in_vsync_range(const struct drm_display_mode * mode,const struct edid * edid,const u8 * t)3566b1f559ecSChris Wilson mode_in_vsync_range(const struct drm_display_mode *mode,
3567c14e7241SJani Nikula 		    const struct edid *edid, const u8 *t)
3568b17e52efSAdam Jackson {
3569b17e52efSAdam Jackson 	int vsync, vmin, vmax;
3570b17e52efSAdam Jackson 
3571b17e52efSAdam Jackson 	vmin = t[5];
3572b17e52efSAdam Jackson 	if (edid->revision >= 4)
3573b17e52efSAdam Jackson 	    vmin += ((t[4] & 0x01) ? 255 : 0);
3574b17e52efSAdam Jackson 	vmax = t[6];
3575b17e52efSAdam Jackson 	if (edid->revision >= 4)
3576b17e52efSAdam Jackson 	    vmax += ((t[4] & 0x02) ? 255 : 0);
3577b17e52efSAdam Jackson 	vsync = drm_mode_vrefresh(mode);
3578b17e52efSAdam Jackson 
3579b17e52efSAdam Jackson 	return (vsync <= vmax && vsync >= vmin);
3580b17e52efSAdam Jackson }
3581b17e52efSAdam Jackson 
3582b17e52efSAdam Jackson static u32
range_pixel_clock(const struct edid * edid,const u8 * t)3583c14e7241SJani Nikula range_pixel_clock(const struct edid *edid, const u8 *t)
3584b17e52efSAdam Jackson {
3585b17e52efSAdam Jackson 	/* unspecified */
3586b17e52efSAdam Jackson 	if (t[9] == 0 || t[9] == 255)
3587b17e52efSAdam Jackson 		return 0;
3588b17e52efSAdam Jackson 
3589b17e52efSAdam Jackson 	/* 1.4 with CVT support gives us real precision, yay */
3590afd4429eSVille Syrjälä 	if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG)
3591b17e52efSAdam Jackson 		return (t[9] * 10000) - ((t[12] >> 2) * 250);
3592b17e52efSAdam Jackson 
3593b17e52efSAdam Jackson 	/* 1.3 is pathetic, so fuzz up a bit */
3594b17e52efSAdam Jackson 	return t[9] * 10000 + 5001;
3595b17e52efSAdam Jackson }
3596b17e52efSAdam Jackson 
mode_in_range(const struct drm_display_mode * mode,const struct drm_edid * drm_edid,const struct detailed_timing * timing)3597874d98eeSJani Nikula static bool mode_in_range(const struct drm_display_mode *mode,
3598874d98eeSJani Nikula 			  const struct drm_edid *drm_edid,
3599fcfb2ea1SJani Nikula 			  const struct detailed_timing *timing)
360007a5e632SAdam Jackson {
3601874d98eeSJani Nikula 	const struct edid *edid = drm_edid->edid;
3602b17e52efSAdam Jackson 	u32 max_clock;
3603fcfb2ea1SJani Nikula 	const u8 *t = (const u8 *)timing;
360407a5e632SAdam Jackson 
3605b17e52efSAdam Jackson 	if (!mode_in_hsync_range(mode, edid, t))
360607a5e632SAdam Jackson 		return false;
360707a5e632SAdam Jackson 
3608b17e52efSAdam Jackson 	if (!mode_in_vsync_range(mode, edid, t))
360907a5e632SAdam Jackson 		return false;
361007a5e632SAdam Jackson 
3611b17e52efSAdam Jackson 	if ((max_clock = range_pixel_clock(edid, t)))
361207a5e632SAdam Jackson 		if (mode->clock > max_clock)
361307a5e632SAdam Jackson 			return false;
3614b17e52efSAdam Jackson 
3615b17e52efSAdam Jackson 	/* 1.4 max horizontal check */
3616afd4429eSVille Syrjälä 	if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG)
3617b17e52efSAdam Jackson 		if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
3618b17e52efSAdam Jackson 			return false;
3619b17e52efSAdam Jackson 
3620874d98eeSJani Nikula 	if (mode_is_rb(mode) && !drm_monitor_supports_rb(drm_edid))
3621b17e52efSAdam Jackson 		return false;
362207a5e632SAdam Jackson 
362307a5e632SAdam Jackson 	return true;
362407a5e632SAdam Jackson }
362507a5e632SAdam Jackson 
valid_inferred_mode(const struct drm_connector * connector,const struct drm_display_mode * mode)36267b668ebeSTakashi Iwai static bool valid_inferred_mode(const struct drm_connector *connector,
36277b668ebeSTakashi Iwai 				const struct drm_display_mode *mode)
36287b668ebeSTakashi Iwai {
362985f8fcd6SVille Syrjälä 	const struct drm_display_mode *m;
36307b668ebeSTakashi Iwai 	bool ok = false;
36317b668ebeSTakashi Iwai 
36327b668ebeSTakashi Iwai 	list_for_each_entry(m, &connector->probed_modes, head) {
36337b668ebeSTakashi Iwai 		if (mode->hdisplay == m->hdisplay &&
36347b668ebeSTakashi Iwai 		    mode->vdisplay == m->vdisplay &&
36357b668ebeSTakashi Iwai 		    drm_mode_vrefresh(mode) == drm_mode_vrefresh(m))
36367b668ebeSTakashi Iwai 			return false; /* duplicated */
36377b668ebeSTakashi Iwai 		if (mode->hdisplay <= m->hdisplay &&
36387b668ebeSTakashi Iwai 		    mode->vdisplay <= m->vdisplay)
36397b668ebeSTakashi Iwai 			ok = true;
36407b668ebeSTakashi Iwai 	}
36417b668ebeSTakashi Iwai 	return ok;
36427b668ebeSTakashi Iwai }
36437b668ebeSTakashi Iwai 
drm_dmt_modes_for_range(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct detailed_timing * timing)3644084c7a7cSJani Nikula static int drm_dmt_modes_for_range(struct drm_connector *connector,
3645084c7a7cSJani Nikula 				   const struct drm_edid *drm_edid,
3646fcfb2ea1SJani Nikula 				   const struct detailed_timing *timing)
364707a5e632SAdam Jackson {
364807a5e632SAdam Jackson 	int i, modes = 0;
364907a5e632SAdam Jackson 	struct drm_display_mode *newmode;
365007a5e632SAdam Jackson 	struct drm_device *dev = connector->dev;
365107a5e632SAdam Jackson 
3652a6b21831SThierry Reding 	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
3653874d98eeSJani Nikula 		if (mode_in_range(drm_dmt_modes + i, drm_edid, timing) &&
36547b668ebeSTakashi Iwai 		    valid_inferred_mode(connector, drm_dmt_modes + i)) {
365507a5e632SAdam Jackson 			newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
365607a5e632SAdam Jackson 			if (newmode) {
365707a5e632SAdam Jackson 				drm_mode_probed_add(connector, newmode);
365807a5e632SAdam Jackson 				modes++;
365907a5e632SAdam Jackson 			}
366007a5e632SAdam Jackson 		}
366107a5e632SAdam Jackson 	}
366207a5e632SAdam Jackson 
366307a5e632SAdam Jackson 	return modes;
366407a5e632SAdam Jackson }
366507a5e632SAdam Jackson 
3666c09dedb7STakashi Iwai /* fix up 1366x768 mode from 1368x768;
3667c09dedb7STakashi Iwai  * GFT/CVT can't express 1366 width which isn't dividable by 8
3668c09dedb7STakashi Iwai  */
drm_mode_fixup_1366x768(struct drm_display_mode * mode)3669969218feSTakashi Iwai void drm_mode_fixup_1366x768(struct drm_display_mode *mode)
3670c09dedb7STakashi Iwai {
3671c09dedb7STakashi Iwai 	if (mode->hdisplay == 1368 && mode->vdisplay == 768) {
3672c09dedb7STakashi Iwai 		mode->hdisplay = 1366;
3673c09dedb7STakashi Iwai 		mode->hsync_start--;
3674c09dedb7STakashi Iwai 		mode->hsync_end--;
3675c09dedb7STakashi Iwai 		drm_mode_set_name(mode);
3676c09dedb7STakashi Iwai 	}
3677c09dedb7STakashi Iwai }
3678c09dedb7STakashi Iwai 
drm_gtf_modes_for_range(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct detailed_timing * timing)3679a77f7c89SJani Nikula static int drm_gtf_modes_for_range(struct drm_connector *connector,
3680a77f7c89SJani Nikula 				   const struct drm_edid *drm_edid,
3681fcfb2ea1SJani Nikula 				   const struct detailed_timing *timing)
3682b309bd37SAdam Jackson {
3683b309bd37SAdam Jackson 	int i, modes = 0;
3684b309bd37SAdam Jackson 	struct drm_display_mode *newmode;
3685b309bd37SAdam Jackson 	struct drm_device *dev = connector->dev;
3686b309bd37SAdam Jackson 
3687a6b21831SThierry Reding 	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
3688b309bd37SAdam Jackson 		const struct minimode *m = &extra_modes[i];
3689948de842SSuraj Upadhyay 
3690b309bd37SAdam Jackson 		newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
3691fc48f169STakashi Iwai 		if (!newmode)
3692fc48f169STakashi Iwai 			return modes;
3693b309bd37SAdam Jackson 
3694969218feSTakashi Iwai 		drm_mode_fixup_1366x768(newmode);
3695874d98eeSJani Nikula 		if (!mode_in_range(newmode, drm_edid, timing) ||
36967b668ebeSTakashi Iwai 		    !valid_inferred_mode(connector, newmode)) {
3697b309bd37SAdam Jackson 			drm_mode_destroy(dev, newmode);
3698b309bd37SAdam Jackson 			continue;
3699b309bd37SAdam Jackson 		}
3700b309bd37SAdam Jackson 
3701b309bd37SAdam Jackson 		drm_mode_probed_add(connector, newmode);
3702b309bd37SAdam Jackson 		modes++;
3703b309bd37SAdam Jackson 	}
3704b309bd37SAdam Jackson 
3705b309bd37SAdam Jackson 	return modes;
3706b309bd37SAdam Jackson }
3707b309bd37SAdam Jackson 
drm_gtf2_modes_for_range(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct detailed_timing * timing)37089ed15f91SVille Syrjälä static int drm_gtf2_modes_for_range(struct drm_connector *connector,
37099ed15f91SVille Syrjälä 				    const struct drm_edid *drm_edid,
37109ed15f91SVille Syrjälä 				    const struct detailed_timing *timing)
37119ed15f91SVille Syrjälä {
37129ed15f91SVille Syrjälä 	int i, modes = 0;
37139ed15f91SVille Syrjälä 	struct drm_display_mode *newmode;
37149ed15f91SVille Syrjälä 	struct drm_device *dev = connector->dev;
37159ed15f91SVille Syrjälä 
37169ed15f91SVille Syrjälä 	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
37179ed15f91SVille Syrjälä 		const struct minimode *m = &extra_modes[i];
37189ed15f91SVille Syrjälä 
37199ed15f91SVille Syrjälä 		newmode = drm_gtf2_mode(dev, drm_edid, m->w, m->h, m->r);
37209ed15f91SVille Syrjälä 		if (!newmode)
37219ed15f91SVille Syrjälä 			return modes;
37229ed15f91SVille Syrjälä 
37239ed15f91SVille Syrjälä 		drm_mode_fixup_1366x768(newmode);
37249ed15f91SVille Syrjälä 		if (!mode_in_range(newmode, drm_edid, timing) ||
37259ed15f91SVille Syrjälä 		    !valid_inferred_mode(connector, newmode)) {
37269ed15f91SVille Syrjälä 			drm_mode_destroy(dev, newmode);
37279ed15f91SVille Syrjälä 			continue;
37289ed15f91SVille Syrjälä 		}
37299ed15f91SVille Syrjälä 
37309ed15f91SVille Syrjälä 		drm_mode_probed_add(connector, newmode);
37319ed15f91SVille Syrjälä 		modes++;
37329ed15f91SVille Syrjälä 	}
37339ed15f91SVille Syrjälä 
37349ed15f91SVille Syrjälä 	return modes;
37359ed15f91SVille Syrjälä }
37369ed15f91SVille Syrjälä 
drm_cvt_modes_for_range(struct drm_connector * connector,const struct drm_edid * drm_edid,const struct detailed_timing * timing)37377428bfbdSJani Nikula static int drm_cvt_modes_for_range(struct drm_connector *connector,
37387428bfbdSJani Nikula 				   const struct drm_edid *drm_edid,
3739fcfb2ea1SJani Nikula 				   const struct detailed_timing *timing)
3740b309bd37SAdam Jackson {
3741b309bd37SAdam Jackson 	int i, modes = 0;
3742b309bd37SAdam Jackson 	struct drm_display_mode *newmode;
3743b309bd37SAdam Jackson 	struct drm_device *dev = connector->dev;
3744874d98eeSJani Nikula 	bool rb = drm_monitor_supports_rb(drm_edid);
3745b309bd37SAdam Jackson 
3746a6b21831SThierry Reding 	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
3747b309bd37SAdam Jackson 		const struct minimode *m = &extra_modes[i];
3748948de842SSuraj Upadhyay 
3749b309bd37SAdam Jackson 		newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
3750fc48f169STakashi Iwai 		if (!newmode)
3751fc48f169STakashi Iwai 			return modes;
3752b309bd37SAdam Jackson 
3753969218feSTakashi Iwai 		drm_mode_fixup_1366x768(newmode);
3754874d98eeSJani Nikula 		if (!mode_in_range(newmode, drm_edid, timing) ||
37557b668ebeSTakashi Iwai 		    !valid_inferred_mode(connector, newmode)) {
3756b309bd37SAdam Jackson 			drm_mode_destroy(dev, newmode);
3757b309bd37SAdam Jackson 			continue;
3758b309bd37SAdam Jackson 		}
3759b309bd37SAdam Jackson 
3760b309bd37SAdam Jackson 		drm_mode_probed_add(connector, newmode);
3761b309bd37SAdam Jackson 		modes++;
3762b309bd37SAdam Jackson 	}
3763b309bd37SAdam Jackson 
3764b309bd37SAdam Jackson 	return modes;
3765b309bd37SAdam Jackson }
3766b309bd37SAdam Jackson 
376713931579SAdam Jackson static void
do_inferred_modes(const struct detailed_timing * timing,void * c)37684194442dSJani Nikula do_inferred_modes(const struct detailed_timing *timing, void *c)
37699340d8cfSAdam Jackson {
377013931579SAdam Jackson 	struct detailed_mode_closure *closure = c;
3771fcfb2ea1SJani Nikula 	const struct detailed_non_pixel *data = &timing->data.other_data;
3772fcfb2ea1SJani Nikula 	const struct detailed_data_monitor_range *range = &data->data.range;
37739340d8cfSAdam Jackson 
3774e379814bSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
3775cb21aafeSAdam Jackson 		return;
3776cb21aafeSAdam Jackson 
3777cd4cd3deSAdam Jackson 	closure->modes += drm_dmt_modes_for_range(closure->connector,
3778084c7a7cSJani Nikula 						  closure->drm_edid,
377913931579SAdam Jackson 						  timing);
3780b309bd37SAdam Jackson 
3781dd3abfe4SVille Syrjälä 	if (closure->drm_edid->edid->revision < 2)
3782b309bd37SAdam Jackson 		return; /* GTF not defined yet */
3783b309bd37SAdam Jackson 
3784b309bd37SAdam Jackson 	switch (range->flags) {
37859ed15f91SVille Syrjälä 	case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG:
37869ed15f91SVille Syrjälä 		closure->modes += drm_gtf2_modes_for_range(closure->connector,
37879ed15f91SVille Syrjälä 							   closure->drm_edid,
37889ed15f91SVille Syrjälä 							   timing);
37899ed15f91SVille Syrjälä 		break;
3790afd4429eSVille Syrjälä 	case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG:
3791b309bd37SAdam Jackson 		closure->modes += drm_gtf_modes_for_range(closure->connector,
3792a77f7c89SJani Nikula 							  closure->drm_edid,
3793b309bd37SAdam Jackson 							  timing);
3794b309bd37SAdam Jackson 		break;
3795afd4429eSVille Syrjälä 	case DRM_EDID_CVT_SUPPORT_FLAG:
3796dd3abfe4SVille Syrjälä 		if (closure->drm_edid->edid->revision < 4)
3797b309bd37SAdam Jackson 			break;
3798b309bd37SAdam Jackson 
3799b309bd37SAdam Jackson 		closure->modes += drm_cvt_modes_for_range(closure->connector,
38007428bfbdSJani Nikula 							  closure->drm_edid,
3801b309bd37SAdam Jackson 							  timing);
3802b309bd37SAdam Jackson 		break;
3803afd4429eSVille Syrjälä 	case DRM_EDID_RANGE_LIMITS_ONLY_FLAG:
3804b309bd37SAdam Jackson 	default:
3805b309bd37SAdam Jackson 		break;
3806b309bd37SAdam Jackson 	}
38079340d8cfSAdam Jackson }
38089340d8cfSAdam Jackson 
add_inferred_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)380940f71f5bSJani Nikula static int add_inferred_modes(struct drm_connector *connector,
381040f71f5bSJani Nikula 			      const struct drm_edid *drm_edid)
381113931579SAdam Jackson {
381213931579SAdam Jackson 	struct detailed_mode_closure closure = {
3813d456ea2eSJulia Lawall 		.connector = connector,
3814dd0f4470SJani Nikula 		.drm_edid = drm_edid,
381513931579SAdam Jackson 	};
381613931579SAdam Jackson 
3817dd3abfe4SVille Syrjälä 	if (drm_edid->edid->revision >= 1)
381845aa2336SJani Nikula 		drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure);
381913931579SAdam Jackson 
382013931579SAdam Jackson 	return closure.modes;
38219340d8cfSAdam Jackson }
38229340d8cfSAdam Jackson 
38232255be14SAdam Jackson static int
drm_est3_modes(struct drm_connector * connector,const struct detailed_timing * timing)3824fcfb2ea1SJani Nikula drm_est3_modes(struct drm_connector *connector, const struct detailed_timing *timing)
38252255be14SAdam Jackson {
38262255be14SAdam Jackson 	int i, j, m, modes = 0;
38272255be14SAdam Jackson 	struct drm_display_mode *mode;
3828fcfb2ea1SJani Nikula 	const u8 *est = ((const u8 *)timing) + 6;
38292255be14SAdam Jackson 
38302255be14SAdam Jackson 	for (i = 0; i < 6; i++) {
3831891a7469SVille Syrjälä 		for (j = 7; j >= 0; j--) {
38322255be14SAdam Jackson 			m = (i * 8) + (7 - j);
3833aa9f56b6SLinus Torvalds 			if (m >= ARRAY_SIZE(est3_modes))
38342255be14SAdam Jackson 				break;
38352255be14SAdam Jackson 			if (est[i] & (1 << j)) {
38361d42bbc8SDave Airlie 				mode = drm_mode_find_dmt(connector->dev,
38372255be14SAdam Jackson 							 est3_modes[m].w,
38382255be14SAdam Jackson 							 est3_modes[m].h,
3839f6e252baSAdam Jackson 							 est3_modes[m].r,
3840f6e252baSAdam Jackson 							 est3_modes[m].rb);
38412255be14SAdam Jackson 				if (mode) {
38422255be14SAdam Jackson 					drm_mode_probed_add(connector, mode);
38432255be14SAdam Jackson 					modes++;
38442255be14SAdam Jackson 				}
38452255be14SAdam Jackson 			}
38462255be14SAdam Jackson 		}
38472255be14SAdam Jackson 	}
38482255be14SAdam Jackson 
38492255be14SAdam Jackson 	return modes;
38502255be14SAdam Jackson }
38512255be14SAdam Jackson 
385213931579SAdam Jackson static void
do_established_modes(const struct detailed_timing * timing,void * c)38534194442dSJani Nikula do_established_modes(const struct detailed_timing *timing, void *c)
3854f453ba04SDave Airlie {
385513931579SAdam Jackson 	struct detailed_mode_closure *closure = c;
3856f453ba04SDave Airlie 
3857e379814bSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_EST_TIMINGS))
3858a7a131acSVille Syrjälä 		return;
3859a7a131acSVille Syrjälä 
386013931579SAdam Jackson 	closure->modes += drm_est3_modes(closure->connector, timing);
38619cf00977SAdam Jackson }
38629cf00977SAdam Jackson 
386317edb8e1SJani Nikula /*
386417edb8e1SJani Nikula  * Get established modes from EDID and add them. Each EDID block contains a
386517edb8e1SJani Nikula  * bitmap of the supported "established modes" list (defined above). Tease them
386617edb8e1SJani Nikula  * out and add them to the global modes list.
386713931579SAdam Jackson  */
add_established_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)386840f71f5bSJani Nikula static int add_established_modes(struct drm_connector *connector,
386940f71f5bSJani Nikula 				 const struct drm_edid *drm_edid)
387013931579SAdam Jackson {
387113931579SAdam Jackson 	struct drm_device *dev = connector->dev;
387240f71f5bSJani Nikula 	const struct edid *edid = drm_edid->edid;
387313931579SAdam Jackson 	unsigned long est_bits = edid->established_timings.t1 |
387413931579SAdam Jackson 		(edid->established_timings.t2 << 8) |
387513931579SAdam Jackson 		((edid->established_timings.mfg_rsvd & 0x80) << 9);
387613931579SAdam Jackson 	int i, modes = 0;
387713931579SAdam Jackson 	struct detailed_mode_closure closure = {
3878d456ea2eSJulia Lawall 		.connector = connector,
3879dd0f4470SJani Nikula 		.drm_edid = drm_edid,
388013931579SAdam Jackson 	};
388113931579SAdam Jackson 
388213931579SAdam Jackson 	for (i = 0; i <= EDID_EST_TIMINGS; i++) {
388313931579SAdam Jackson 		if (est_bits & (1<<i)) {
388413931579SAdam Jackson 			struct drm_display_mode *newmode;
3885948de842SSuraj Upadhyay 
388613931579SAdam Jackson 			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
388713931579SAdam Jackson 			if (newmode) {
388813931579SAdam Jackson 				drm_mode_probed_add(connector, newmode);
388913931579SAdam Jackson 				modes++;
389013931579SAdam Jackson 			}
389113931579SAdam Jackson 		}
389213931579SAdam Jackson 	}
389313931579SAdam Jackson 
3894dd3abfe4SVille Syrjälä 	if (edid->revision >= 1)
389545aa2336SJani Nikula 		drm_for_each_detailed_block(drm_edid, do_established_modes,
3896eed628f1SJani Nikula 					    &closure);
389713931579SAdam Jackson 
389813931579SAdam Jackson 	return modes + closure.modes;
389913931579SAdam Jackson }
390013931579SAdam Jackson 
390113931579SAdam Jackson static void
do_standard_modes(const struct detailed_timing * timing,void * c)39024194442dSJani Nikula do_standard_modes(const struct detailed_timing *timing, void *c)
390313931579SAdam Jackson {
390413931579SAdam Jackson 	struct detailed_mode_closure *closure = c;
3905fcfb2ea1SJani Nikula 	const struct detailed_non_pixel *data = &timing->data.other_data;
390613931579SAdam Jackson 	struct drm_connector *connector = closure->connector;
390713931579SAdam Jackson 	int i;
3908a7a131acSVille Syrjälä 
3909e379814bSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES))
3910a7a131acSVille Syrjälä 		return;
3911a7a131acSVille Syrjälä 
39129cf00977SAdam Jackson 	for (i = 0; i < 6; i++) {
3913fcfb2ea1SJani Nikula 		const struct std_timing *std = &data->data.timings[i];
3914f453ba04SDave Airlie 		struct drm_display_mode *newmode;
3915f453ba04SDave Airlie 
391667d87facSJani Nikula 		newmode = drm_mode_std(connector, closure->drm_edid, std);
3917f453ba04SDave Airlie 		if (newmode) {
3918f453ba04SDave Airlie 			drm_mode_probed_add(connector, newmode);
391913931579SAdam Jackson 			closure->modes++;
392013931579SAdam Jackson 		}
392113931579SAdam Jackson 	}
392213931579SAdam Jackson }
392313931579SAdam Jackson 
392417edb8e1SJani Nikula /*
392517edb8e1SJani Nikula  * Get standard modes from EDID and add them. Standard modes can be calculated
392617edb8e1SJani Nikula  * using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and
392717edb8e1SJani Nikula  * add them to the list.
392813931579SAdam Jackson  */
add_standard_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)392940f71f5bSJani Nikula static int add_standard_modes(struct drm_connector *connector,
393040f71f5bSJani Nikula 			      const struct drm_edid *drm_edid)
393113931579SAdam Jackson {
393213931579SAdam Jackson 	int i, modes = 0;
393313931579SAdam Jackson 	struct detailed_mode_closure closure = {
3934d456ea2eSJulia Lawall 		.connector = connector,
3935dd0f4470SJani Nikula 		.drm_edid = drm_edid,
393613931579SAdam Jackson 	};
393713931579SAdam Jackson 
393813931579SAdam Jackson 	for (i = 0; i < EDID_STD_TIMINGS; i++) {
393913931579SAdam Jackson 		struct drm_display_mode *newmode;
394013931579SAdam Jackson 
394167d87facSJani Nikula 		newmode = drm_mode_std(connector, drm_edid,
394240f71f5bSJani Nikula 				       &drm_edid->edid->standard_timings[i]);
394313931579SAdam Jackson 		if (newmode) {
394413931579SAdam Jackson 			drm_mode_probed_add(connector, newmode);
3945f453ba04SDave Airlie 			modes++;
3946f453ba04SDave Airlie 		}
3947f453ba04SDave Airlie 	}
394813931579SAdam Jackson 
3949dd3abfe4SVille Syrjälä 	if (drm_edid->edid->revision >= 1)
395045aa2336SJani Nikula 		drm_for_each_detailed_block(drm_edid, do_standard_modes,
395113931579SAdam Jackson 					    &closure);
395213931579SAdam Jackson 
395313931579SAdam Jackson 	/* XXX should also look for standard codes in VTB blocks */
395413931579SAdam Jackson 
395513931579SAdam Jackson 	return modes + closure.modes;
395613931579SAdam Jackson }
395713931579SAdam Jackson 
drm_cvt_modes(struct drm_connector * connector,const struct detailed_timing * timing)3958f453ba04SDave Airlie static int drm_cvt_modes(struct drm_connector *connector,
3959fcfb2ea1SJani Nikula 			 const struct detailed_timing *timing)
3960f453ba04SDave Airlie {
3961f453ba04SDave Airlie 	int i, j, modes = 0;
3962f453ba04SDave Airlie 	struct drm_display_mode *newmode;
3963f453ba04SDave Airlie 	struct drm_device *dev = connector->dev;
3964fcfb2ea1SJani Nikula 	const struct cvt_timing *cvt;
3965bce1eb50SColin Ian King 	static const int rates[] = { 60, 85, 75, 60, 50 };
3966f453ba04SDave Airlie 	const u8 empty[3] = { 0, 0, 0 };
3967f453ba04SDave Airlie 
3968f453ba04SDave Airlie 	for (i = 0; i < 4; i++) {
39693f649ab7SKees Cook 		int width, height;
3970948de842SSuraj Upadhyay 
3971f453ba04SDave Airlie 		cvt = &(timing->data.other_data.data.cvt[i]);
3972f453ba04SDave Airlie 
3973f453ba04SDave Airlie 		if (!memcmp(cvt->code, empty, 3))
3974f453ba04SDave Airlie 			continue;
3975f453ba04SDave Airlie 
3976f453ba04SDave Airlie 		height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
3977f453ba04SDave Airlie 		switch (cvt->code[1] & 0x0c) {
3978d652d5f1SLinus Torvalds 		/* default - because compiler doesn't see that we've enumerated all cases */
3979d652d5f1SLinus Torvalds 		default:
3980f453ba04SDave Airlie 		case 0x00:
3981f453ba04SDave Airlie 			width = height * 4 / 3;
3982f453ba04SDave Airlie 			break;
3983f453ba04SDave Airlie 		case 0x04:
3984f453ba04SDave Airlie 			width = height * 16 / 9;
3985f453ba04SDave Airlie 			break;
3986f453ba04SDave Airlie 		case 0x08:
3987f453ba04SDave Airlie 			width = height * 16 / 10;
3988f453ba04SDave Airlie 			break;
3989f453ba04SDave Airlie 		case 0x0c:
3990f453ba04SDave Airlie 			width = height * 15 / 9;
3991f453ba04SDave Airlie 			break;
3992f453ba04SDave Airlie 		}
3993f453ba04SDave Airlie 
3994f453ba04SDave Airlie 		for (j = 1; j < 5; j++) {
3995f453ba04SDave Airlie 			if (cvt->code[2] & (1 << j)) {
3996f453ba04SDave Airlie 				newmode = drm_cvt_mode(dev, width, height,
3997f453ba04SDave Airlie 						       rates[j], j == 0,
3998f453ba04SDave Airlie 						       false, false);
3999f453ba04SDave Airlie 				if (newmode) {
4000f453ba04SDave Airlie 					drm_mode_probed_add(connector, newmode);
4001f453ba04SDave Airlie 					modes++;
4002f453ba04SDave Airlie 				}
4003f453ba04SDave Airlie 			}
4004f453ba04SDave Airlie 		}
4005f453ba04SDave Airlie 	}
4006f453ba04SDave Airlie 
4007f453ba04SDave Airlie 	return modes;
4008f453ba04SDave Airlie }
4009f453ba04SDave Airlie 
401013931579SAdam Jackson static void
do_cvt_mode(const struct detailed_timing * timing,void * c)40114194442dSJani Nikula do_cvt_mode(const struct detailed_timing *timing, void *c)
401213931579SAdam Jackson {
401313931579SAdam Jackson 	struct detailed_mode_closure *closure = c;
401413931579SAdam Jackson 
4015e379814bSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_CVT_3BYTE))
4016a7a131acSVille Syrjälä 		return;
4017a7a131acSVille Syrjälä 
401813931579SAdam Jackson 	closure->modes += drm_cvt_modes(closure->connector, timing);
401913931579SAdam Jackson }
4020f453ba04SDave Airlie 
4021f453ba04SDave Airlie static int
add_cvt_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)402240f71f5bSJani Nikula add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid)
4023f453ba04SDave Airlie {
402413931579SAdam Jackson 	struct detailed_mode_closure closure = {
4025d456ea2eSJulia Lawall 		.connector = connector,
4026dd0f4470SJani Nikula 		.drm_edid = drm_edid,
402713931579SAdam Jackson 	};
4028f453ba04SDave Airlie 
4029dd3abfe4SVille Syrjälä 	if (drm_edid->edid->revision >= 3)
403045aa2336SJani Nikula 		drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure);
403113931579SAdam Jackson 
403213931579SAdam Jackson 	/* XXX should also look for CVT codes in VTB blocks */
403313931579SAdam Jackson 
403413931579SAdam Jackson 	return closure.modes;
40359cf00977SAdam Jackson }
40369cf00977SAdam Jackson 
4037e1e7bc48SJani Nikula static void fixup_detailed_cea_mode_clock(struct drm_connector *connector,
4038e1e7bc48SJani Nikula 					  struct drm_display_mode *mode);
4039fa3a7340SVille Syrjälä 
404013931579SAdam Jackson static void
do_detailed_mode(const struct detailed_timing * timing,void * c)40414194442dSJani Nikula do_detailed_mode(const struct detailed_timing *timing, void *c)
40429cf00977SAdam Jackson {
404313931579SAdam Jackson 	struct detailed_mode_closure *closure = c;
40449cf00977SAdam Jackson 	struct drm_display_mode *newmode;
40459cf00977SAdam Jackson 
4046a9b1f15fSJani Nikula 	if (!is_detailed_timing_descriptor(timing))
4047f447dd1fSVille Syrjälä 		return;
4048f447dd1fSVille Syrjälä 
4049e1e7bc48SJani Nikula 	newmode = drm_mode_detailed(closure->connector,
40504959b693SJani Nikula 				    closure->drm_edid, timing);
4051ebb177d2SDave Airlie 	if (!newmode)
405213931579SAdam Jackson 		return;
40539cf00977SAdam Jackson 
405413931579SAdam Jackson 	if (closure->preferred)
4055f453ba04SDave Airlie 		newmode->type |= DRM_MODE_TYPE_PREFERRED;
4056f453ba04SDave Airlie 
4057fa3a7340SVille Syrjälä 	/*
4058fa3a7340SVille Syrjälä 	 * Detailed modes are limited to 10kHz pixel clock resolution,
4059fa3a7340SVille Syrjälä 	 * so fix up anything that looks like CEA/HDMI mode, but the clock
4060fa3a7340SVille Syrjälä 	 * is just slightly off.
4061fa3a7340SVille Syrjälä 	 */
4062e1e7bc48SJani Nikula 	fixup_detailed_cea_mode_clock(closure->connector, newmode);
4063fa3a7340SVille Syrjälä 
406413931579SAdam Jackson 	drm_mode_probed_add(closure->connector, newmode);
406513931579SAdam Jackson 	closure->modes++;
4066c2925bdeSGustavo A. R. Silva 	closure->preferred = false;
406713931579SAdam Jackson }
4068882f0219SZhao Yakui 
406913931579SAdam Jackson /*
407013931579SAdam Jackson  * add_detailed_modes - Add modes from detailed timings
4071f453ba04SDave Airlie  * @connector: attached connector
407240f71f5bSJani Nikula  * @drm_edid: EDID block to scan
4073f453ba04SDave Airlie  */
add_detailed_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)407440f71f5bSJani Nikula static int add_detailed_modes(struct drm_connector *connector,
40754959b693SJani Nikula 			      const struct drm_edid *drm_edid)
4076f453ba04SDave Airlie {
407713931579SAdam Jackson 	struct detailed_mode_closure closure = {
4078d456ea2eSJulia Lawall 		.connector = connector,
4079dd0f4470SJani Nikula 		.drm_edid = drm_edid,
408013931579SAdam Jackson 	};
4081f453ba04SDave Airlie 
4082dd3abfe4SVille Syrjälä 	if (drm_edid->edid->revision >= 4)
4083f72f9529SVille Syrjälä 		closure.preferred = true; /* first detailed timing is always preferred */
4084f72f9529SVille Syrjälä 	else
408513931579SAdam Jackson 		closure.preferred =
4086f72f9529SVille Syrjälä 			drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING;
4087a327f6b8SAdam Jackson 
408845aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure);
4089f453ba04SDave Airlie 
409013931579SAdam Jackson 	return closure.modes;
4091882f0219SZhao Yakui }
4092f453ba04SDave Airlie 
40939d72b7e2SJani Nikula /* CTA-861-H Table 60 - CTA Tag Codes */
40949d72b7e2SJani Nikula #define CTA_DB_AUDIO			1
40959d72b7e2SJani Nikula #define CTA_DB_VIDEO			2
40969d72b7e2SJani Nikula #define CTA_DB_VENDOR			3
40979d72b7e2SJani Nikula #define CTA_DB_SPEAKER			4
40989d72b7e2SJani Nikula #define CTA_DB_EXTENDED_TAG		7
40999d72b7e2SJani Nikula 
41009d72b7e2SJani Nikula /* CTA-861-H Table 62 - CTA Extended Tag Codes */
41019d72b7e2SJani Nikula #define CTA_EXT_DB_VIDEO_CAP		0
41029d72b7e2SJani Nikula #define CTA_EXT_DB_VENDOR		1
41039d72b7e2SJani Nikula #define CTA_EXT_DB_HDR_STATIC_METADATA	6
41049d72b7e2SJani Nikula #define CTA_EXT_DB_420_VIDEO_DATA	14
41059d72b7e2SJani Nikula #define CTA_EXT_DB_420_VIDEO_CAP_MAP	15
410618e3c1d5SJani Nikula #define CTA_EXT_DB_HF_EEODB		0x78
41079d72b7e2SJani Nikula #define CTA_EXT_DB_HF_SCDB		0x79
41089d72b7e2SJani Nikula 
41098fe9790dSZhenyu Wang #define EDID_BASIC_AUDIO	(1 << 6)
4110a988bc72SLars-Peter Clausen #define EDID_CEA_YCRCB444	(1 << 5)
4111a988bc72SLars-Peter Clausen #define EDID_CEA_YCRCB422	(1 << 4)
4112b1edd6a6SVille Syrjälä #define EDID_CEA_VCDB_QS	(1 << 6)
41138fe9790dSZhenyu Wang 
4114d4e4a31dSLespiau, Damien /*
41158fe9790dSZhenyu Wang  * Search EDID for CEA extension block.
4116d9ba1b4cSJani Nikula  *
4117d9ba1b4cSJani Nikula  * FIXME: Prefer not returning pointers to raw EDID data.
41188fe9790dSZhenyu Wang  */
drm_find_edid_extension(const struct drm_edid * drm_edid,int ext_id,int * ext_index)4119d9ba1b4cSJani Nikula const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
41208873cfa3SVille Syrjälä 				  int ext_id, int *ext_index)
41218fe9790dSZhenyu Wang {
412243d16d84SJani Nikula 	const u8 *edid_ext = NULL;
41238fe9790dSZhenyu Wang 	int i;
41248fe9790dSZhenyu Wang 
41258fe9790dSZhenyu Wang 	/* No EDID or EDID extensions */
4126d9307f27SJani Nikula 	if (!drm_edid || !drm_edid_extension_block_count(drm_edid))
41278fe9790dSZhenyu Wang 		return NULL;
41288fe9790dSZhenyu Wang 
41298fe9790dSZhenyu Wang 	/* Find CEA extension */
4130d9307f27SJani Nikula 	for (i = *ext_index; i < drm_edid_extension_block_count(drm_edid); i++) {
4131d9307f27SJani Nikula 		edid_ext = drm_edid_extension_block_data(drm_edid, i);
41324ba0f53cSJani Nikula 		if (edid_block_tag(edid_ext) == ext_id)
41338fe9790dSZhenyu Wang 			break;
41348fe9790dSZhenyu Wang 	}
41358fe9790dSZhenyu Wang 
4136d9307f27SJani Nikula 	if (i >= drm_edid_extension_block_count(drm_edid))
41378fe9790dSZhenyu Wang 		return NULL;
41388fe9790dSZhenyu Wang 
41398873cfa3SVille Syrjälä 	*ext_index = i + 1;
41408873cfa3SVille Syrjälä 
41418fe9790dSZhenyu Wang 	return edid_ext;
41428fe9790dSZhenyu Wang }
41438fe9790dSZhenyu Wang 
41446ff1c19fSJani Nikula /* Return true if the EDID has a CTA extension or a DisplayID CTA data block */
drm_edid_has_cta_extension(const struct drm_edid * drm_edid)414540f71f5bSJani Nikula static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid)
4146e28ad544SAndres Rodriguez {
414743d16d84SJani Nikula 	const struct displayid_block *block;
41481ba63cafSJani Nikula 	struct displayid_iter iter;
41491ba63cafSJani Nikula 	int ext_index = 0;
41506ff1c19fSJani Nikula 	bool found = false;
4151e28ad544SAndres Rodriguez 
4152e28ad544SAndres Rodriguez 	/* Look for a top level CEA extension block */
4153d9ba1b4cSJani Nikula 	if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index))
41546ff1c19fSJani Nikula 		return true;
4155e28ad544SAndres Rodriguez 
4156e28ad544SAndres Rodriguez 	/* CEA blocks can also be found embedded in a DisplayID block */
4157d9ba1b4cSJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter);
41581ba63cafSJani Nikula 	displayid_iter_for_each(block, &iter) {
41591ba63cafSJani Nikula 		if (block->tag == DATA_BLOCK_CTA) {
41606ff1c19fSJani Nikula 			found = true;
41611ba63cafSJani Nikula 			break;
4162e28ad544SAndres Rodriguez 		}
4163e28ad544SAndres Rodriguez 	}
41641ba63cafSJani Nikula 	displayid_iter_end(&iter);
4165e28ad544SAndres Rodriguez 
41666ff1c19fSJani Nikula 	return found;
4167e28ad544SAndres Rodriguez }
4168e28ad544SAndres Rodriguez 
cea_mode_for_vic(u8 vic)4169e1cf35b9SMauro Rossi static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic)
41707befe621SVille Syrjälä {
41719212f8eeSVille Syrjälä 	BUILD_BUG_ON(1 + ARRAY_SIZE(edid_cea_modes_1) - 1 != 127);
41729212f8eeSVille Syrjälä 	BUILD_BUG_ON(193 + ARRAY_SIZE(edid_cea_modes_193) - 1 != 219);
41739212f8eeSVille Syrjälä 
41748c1b2bd9SVille Syrjälä 	if (vic >= 1 && vic < 1 + ARRAY_SIZE(edid_cea_modes_1))
41758c1b2bd9SVille Syrjälä 		return &edid_cea_modes_1[vic - 1];
4176f7655d42SVille Syrjälä 	if (vic >= 193 && vic < 193 + ARRAY_SIZE(edid_cea_modes_193))
4177f7655d42SVille Syrjälä 		return &edid_cea_modes_193[vic - 193];
41787befe621SVille Syrjälä 	return NULL;
41797befe621SVille Syrjälä }
41807befe621SVille Syrjälä 
cea_num_vics(void)41817befe621SVille Syrjälä static u8 cea_num_vics(void)
41827befe621SVille Syrjälä {
4183f7655d42SVille Syrjälä 	return 193 + ARRAY_SIZE(edid_cea_modes_193);
41847befe621SVille Syrjälä }
41857befe621SVille Syrjälä 
cea_next_vic(u8 vic)41867befe621SVille Syrjälä static u8 cea_next_vic(u8 vic)
41877befe621SVille Syrjälä {
41888c1b2bd9SVille Syrjälä 	if (++vic == 1 + ARRAY_SIZE(edid_cea_modes_1))
4189f7655d42SVille Syrjälä 		vic = 193;
4190f7655d42SVille Syrjälä 	return vic;
41917befe621SVille Syrjälä }
41927befe621SVille Syrjälä 
4193e6e79209SVille Syrjälä /*
4194e6e79209SVille Syrjälä  * Calculate the alternate clock for the CEA mode
4195e6e79209SVille Syrjälä  * (60Hz vs. 59.94Hz etc.)
4196e6e79209SVille Syrjälä  */
4197e6e79209SVille Syrjälä static unsigned int
cea_mode_alternate_clock(const struct drm_display_mode * cea_mode)4198e6e79209SVille Syrjälä cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
4199e6e79209SVille Syrjälä {
4200e6e79209SVille Syrjälä 	unsigned int clock = cea_mode->clock;
4201e6e79209SVille Syrjälä 
42020425662fSVille Syrjälä 	if (drm_mode_vrefresh(cea_mode) % 6 != 0)
4203e6e79209SVille Syrjälä 		return clock;
4204e6e79209SVille Syrjälä 
4205e6e79209SVille Syrjälä 	/*
4206e6e79209SVille Syrjälä 	 * edid_cea_modes contains the 59.94Hz
4207e6e79209SVille Syrjälä 	 * variant for 240 and 480 line modes,
4208e6e79209SVille Syrjälä 	 * and the 60Hz variant otherwise.
4209e6e79209SVille Syrjälä 	 */
4210e6e79209SVille Syrjälä 	if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
42119afd808cSVille Syrjälä 		clock = DIV_ROUND_CLOSEST(clock * 1001, 1000);
4212e6e79209SVille Syrjälä 	else
42139afd808cSVille Syrjälä 		clock = DIV_ROUND_CLOSEST(clock * 1000, 1001);
4214e6e79209SVille Syrjälä 
4215e6e79209SVille Syrjälä 	return clock;
4216e6e79209SVille Syrjälä }
4217e6e79209SVille Syrjälä 
4218c45a4e46SVille Syrjälä static bool
cea_mode_alternate_timings(u8 vic,struct drm_display_mode * mode)4219c45a4e46SVille Syrjälä cea_mode_alternate_timings(u8 vic, struct drm_display_mode *mode)
4220c45a4e46SVille Syrjälä {
4221c45a4e46SVille Syrjälä 	/*
4222c45a4e46SVille Syrjälä 	 * For certain VICs the spec allows the vertical
4223c45a4e46SVille Syrjälä 	 * front porch to vary by one or two lines.
4224c45a4e46SVille Syrjälä 	 *
4225c45a4e46SVille Syrjälä 	 * cea_modes[] stores the variant with the shortest
4226c45a4e46SVille Syrjälä 	 * vertical front porch. We can adjust the mode to
4227c45a4e46SVille Syrjälä 	 * get the other variants by simply increasing the
4228c45a4e46SVille Syrjälä 	 * vertical front porch length.
4229c45a4e46SVille Syrjälä 	 */
42307befe621SVille Syrjälä 	BUILD_BUG_ON(cea_mode_for_vic(8)->vtotal != 262 ||
42317befe621SVille Syrjälä 		     cea_mode_for_vic(9)->vtotal != 262 ||
42327befe621SVille Syrjälä 		     cea_mode_for_vic(12)->vtotal != 262 ||
42337befe621SVille Syrjälä 		     cea_mode_for_vic(13)->vtotal != 262 ||
42347befe621SVille Syrjälä 		     cea_mode_for_vic(23)->vtotal != 312 ||
42357befe621SVille Syrjälä 		     cea_mode_for_vic(24)->vtotal != 312 ||
42367befe621SVille Syrjälä 		     cea_mode_for_vic(27)->vtotal != 312 ||
42377befe621SVille Syrjälä 		     cea_mode_for_vic(28)->vtotal != 312);
4238c45a4e46SVille Syrjälä 
4239c45a4e46SVille Syrjälä 	if (((vic == 8 || vic == 9 ||
4240c45a4e46SVille Syrjälä 	      vic == 12 || vic == 13) && mode->vtotal < 263) ||
4241c45a4e46SVille Syrjälä 	    ((vic == 23 || vic == 24 ||
4242c45a4e46SVille Syrjälä 	      vic == 27 || vic == 28) && mode->vtotal < 314)) {
4243c45a4e46SVille Syrjälä 		mode->vsync_start++;
4244c45a4e46SVille Syrjälä 		mode->vsync_end++;
4245c45a4e46SVille Syrjälä 		mode->vtotal++;
4246c45a4e46SVille Syrjälä 
4247c45a4e46SVille Syrjälä 		return true;
4248c45a4e46SVille Syrjälä 	}
4249c45a4e46SVille Syrjälä 
4250c45a4e46SVille Syrjälä 	return false;
4251c45a4e46SVille Syrjälä }
4252c45a4e46SVille Syrjälä 
drm_match_cea_mode_clock_tolerance(const struct drm_display_mode * to_match,unsigned int clock_tolerance)42534c6bcf44SVille Syrjälä static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
42544c6bcf44SVille Syrjälä 					     unsigned int clock_tolerance)
42554c6bcf44SVille Syrjälä {
4256357768ccSVille Syrjälä 	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
4257d9278b4cSJani Nikula 	u8 vic;
42584c6bcf44SVille Syrjälä 
42594c6bcf44SVille Syrjälä 	if (!to_match->clock)
42604c6bcf44SVille Syrjälä 		return 0;
42614c6bcf44SVille Syrjälä 
4262357768ccSVille Syrjälä 	if (to_match->picture_aspect_ratio)
4263357768ccSVille Syrjälä 		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
4264357768ccSVille Syrjälä 
42657befe621SVille Syrjälä 	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
4266563c4a75SVille Syrjälä 		struct drm_display_mode cea_mode;
42674c6bcf44SVille Syrjälä 		unsigned int clock1, clock2;
42684c6bcf44SVille Syrjälä 
4269563c4a75SVille Syrjälä 		drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
4270563c4a75SVille Syrjälä 
42714c6bcf44SVille Syrjälä 		/* Check both 60Hz and 59.94Hz */
4272c45a4e46SVille Syrjälä 		clock1 = cea_mode.clock;
4273c45a4e46SVille Syrjälä 		clock2 = cea_mode_alternate_clock(&cea_mode);
42744c6bcf44SVille Syrjälä 
42754c6bcf44SVille Syrjälä 		if (abs(to_match->clock - clock1) > clock_tolerance &&
42764c6bcf44SVille Syrjälä 		    abs(to_match->clock - clock2) > clock_tolerance)
42774c6bcf44SVille Syrjälä 			continue;
42784c6bcf44SVille Syrjälä 
4279c45a4e46SVille Syrjälä 		do {
4280357768ccSVille Syrjälä 			if (drm_mode_match(to_match, &cea_mode, match_flags))
4281d9278b4cSJani Nikula 				return vic;
4282c45a4e46SVille Syrjälä 		} while (cea_mode_alternate_timings(vic, &cea_mode));
42834c6bcf44SVille Syrjälä 	}
42844c6bcf44SVille Syrjälä 
42854c6bcf44SVille Syrjälä 	return 0;
42864c6bcf44SVille Syrjälä }
42874c6bcf44SVille Syrjälä 
428818316c8cSThierry Reding /**
428918316c8cSThierry Reding  * drm_match_cea_mode - look for a CEA mode matching given mode
429018316c8cSThierry Reding  * @to_match: display mode
429118316c8cSThierry Reding  *
4292db6cf833SThierry Reding  * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
429318316c8cSThierry Reding  * mode.
4294a4799037SStephane Marchesin  */
drm_match_cea_mode(const struct drm_display_mode * to_match)429518316c8cSThierry Reding u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
4296a4799037SStephane Marchesin {
4297357768ccSVille Syrjälä 	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
4298d9278b4cSJani Nikula 	u8 vic;
4299a4799037SStephane Marchesin 
4300a90b590eSVille Syrjälä 	if (!to_match->clock)
4301a90b590eSVille Syrjälä 		return 0;
4302a4799037SStephane Marchesin 
4303357768ccSVille Syrjälä 	if (to_match->picture_aspect_ratio)
4304357768ccSVille Syrjälä 		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
4305357768ccSVille Syrjälä 
43067befe621SVille Syrjälä 	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
4307563c4a75SVille Syrjälä 		struct drm_display_mode cea_mode;
4308a90b590eSVille Syrjälä 		unsigned int clock1, clock2;
4309a90b590eSVille Syrjälä 
4310563c4a75SVille Syrjälä 		drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
4311563c4a75SVille Syrjälä 
4312a90b590eSVille Syrjälä 		/* Check both 60Hz and 59.94Hz */
4313c45a4e46SVille Syrjälä 		clock1 = cea_mode.clock;
4314c45a4e46SVille Syrjälä 		clock2 = cea_mode_alternate_clock(&cea_mode);
4315a90b590eSVille Syrjälä 
4316c45a4e46SVille Syrjälä 		if (KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock1) &&
4317c45a4e46SVille Syrjälä 		    KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock2))
4318c45a4e46SVille Syrjälä 			continue;
4319c45a4e46SVille Syrjälä 
4320c45a4e46SVille Syrjälä 		do {
4321357768ccSVille Syrjälä 			if (drm_mode_match(to_match, &cea_mode, match_flags))
4322d9278b4cSJani Nikula 				return vic;
4323c45a4e46SVille Syrjälä 		} while (cea_mode_alternate_timings(vic, &cea_mode));
4324a4799037SStephane Marchesin 	}
4325c45a4e46SVille Syrjälä 
4326a4799037SStephane Marchesin 	return 0;
4327a4799037SStephane Marchesin }
4328a4799037SStephane Marchesin EXPORT_SYMBOL(drm_match_cea_mode);
4329a4799037SStephane Marchesin 
drm_valid_cea_vic(u8 vic)4330d9278b4cSJani Nikula static bool drm_valid_cea_vic(u8 vic)
4331d9278b4cSJani Nikula {
43327befe621SVille Syrjälä 	return cea_mode_for_vic(vic) != NULL;
4333d9278b4cSJani Nikula }
4334d9278b4cSJani Nikula 
drm_get_cea_aspect_ratio(const u8 video_code)433528c03a44SVille Syrjälä static enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
43360967e6a5SVandana Kannan {
43377befe621SVille Syrjälä 	const struct drm_display_mode *mode = cea_mode_for_vic(video_code);
43387befe621SVille Syrjälä 
43397befe621SVille Syrjälä 	if (mode)
43407befe621SVille Syrjälä 		return mode->picture_aspect_ratio;
43417befe621SVille Syrjälä 
43427befe621SVille Syrjälä 	return HDMI_PICTURE_ASPECT_NONE;
43430967e6a5SVandana Kannan }
43440967e6a5SVandana Kannan 
drm_get_hdmi_aspect_ratio(const u8 video_code)4345d2b43473SWayne Lin static enum hdmi_picture_aspect drm_get_hdmi_aspect_ratio(const u8 video_code)
4346d2b43473SWayne Lin {
4347d2b43473SWayne Lin 	return edid_4k_modes[video_code].picture_aspect_ratio;
4348d2b43473SWayne Lin }
4349d2b43473SWayne Lin 
43503f2f6533SLespiau, Damien /*
43513f2f6533SLespiau, Damien  * Calculate the alternate clock for HDMI modes (those from the HDMI vendor
43523f2f6533SLespiau, Damien  * specific block).
43533f2f6533SLespiau, Damien  */
43543f2f6533SLespiau, Damien static unsigned int
hdmi_mode_alternate_clock(const struct drm_display_mode * hdmi_mode)43553f2f6533SLespiau, Damien hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
43563f2f6533SLespiau, Damien {
43573f2f6533SLespiau, Damien 	return cea_mode_alternate_clock(hdmi_mode);
43583f2f6533SLespiau, Damien }
43593f2f6533SLespiau, Damien 
drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode * to_match,unsigned int clock_tolerance)43604c6bcf44SVille Syrjälä static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
43614c6bcf44SVille Syrjälä 					      unsigned int clock_tolerance)
43624c6bcf44SVille Syrjälä {
4363357768ccSVille Syrjälä 	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
4364d9278b4cSJani Nikula 	u8 vic;
43654c6bcf44SVille Syrjälä 
43664c6bcf44SVille Syrjälä 	if (!to_match->clock)
43674c6bcf44SVille Syrjälä 		return 0;
43684c6bcf44SVille Syrjälä 
4369d2b43473SWayne Lin 	if (to_match->picture_aspect_ratio)
4370d2b43473SWayne Lin 		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
4371d2b43473SWayne Lin 
4372d9278b4cSJani Nikula 	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
4373d9278b4cSJani Nikula 		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
43744c6bcf44SVille Syrjälä 		unsigned int clock1, clock2;
43754c6bcf44SVille Syrjälä 
43764c6bcf44SVille Syrjälä 		/* Make sure to also match alternate clocks */
43774c6bcf44SVille Syrjälä 		clock1 = hdmi_mode->clock;
43784c6bcf44SVille Syrjälä 		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
43794c6bcf44SVille Syrjälä 
43804c6bcf44SVille Syrjälä 		if (abs(to_match->clock - clock1) > clock_tolerance &&
43814c6bcf44SVille Syrjälä 		    abs(to_match->clock - clock2) > clock_tolerance)
43824c6bcf44SVille Syrjälä 			continue;
43834c6bcf44SVille Syrjälä 
4384357768ccSVille Syrjälä 		if (drm_mode_match(to_match, hdmi_mode, match_flags))
4385d9278b4cSJani Nikula 			return vic;
43864c6bcf44SVille Syrjälä 	}
43874c6bcf44SVille Syrjälä 
43884c6bcf44SVille Syrjälä 	return 0;
43894c6bcf44SVille Syrjälä }
43904c6bcf44SVille Syrjälä 
43913f2f6533SLespiau, Damien /*
43923f2f6533SLespiau, Damien  * drm_match_hdmi_mode - look for a HDMI mode matching given mode
43933f2f6533SLespiau, Damien  * @to_match: display mode
43943f2f6533SLespiau, Damien  *
43953f2f6533SLespiau, Damien  * An HDMI mode is one defined in the HDMI vendor specific block.
43963f2f6533SLespiau, Damien  *
43973f2f6533SLespiau, Damien  * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
43983f2f6533SLespiau, Damien  */
drm_match_hdmi_mode(const struct drm_display_mode * to_match)43993f2f6533SLespiau, Damien static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
44003f2f6533SLespiau, Damien {
4401357768ccSVille Syrjälä 	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
4402d9278b4cSJani Nikula 	u8 vic;
44033f2f6533SLespiau, Damien 
44043f2f6533SLespiau, Damien 	if (!to_match->clock)
44053f2f6533SLespiau, Damien 		return 0;
44063f2f6533SLespiau, Damien 
4407d2b43473SWayne Lin 	if (to_match->picture_aspect_ratio)
4408d2b43473SWayne Lin 		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
4409d2b43473SWayne Lin 
4410d9278b4cSJani Nikula 	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
4411d9278b4cSJani Nikula 		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
44123f2f6533SLespiau, Damien 		unsigned int clock1, clock2;
44133f2f6533SLespiau, Damien 
44143f2f6533SLespiau, Damien 		/* Make sure to also match alternate clocks */
44153f2f6533SLespiau, Damien 		clock1 = hdmi_mode->clock;
44163f2f6533SLespiau, Damien 		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
44173f2f6533SLespiau, Damien 
44183f2f6533SLespiau, Damien 		if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
44193f2f6533SLespiau, Damien 		     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
4420357768ccSVille Syrjälä 		    drm_mode_match(to_match, hdmi_mode, match_flags))
4421d9278b4cSJani Nikula 			return vic;
44223f2f6533SLespiau, Damien 	}
44233f2f6533SLespiau, Damien 	return 0;
44243f2f6533SLespiau, Damien }
44253f2f6533SLespiau, Damien 
drm_valid_hdmi_vic(u8 vic)4426d9278b4cSJani Nikula static bool drm_valid_hdmi_vic(u8 vic)
4427d9278b4cSJani Nikula {
4428d9278b4cSJani Nikula 	return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
4429d9278b4cSJani Nikula }
4430d9278b4cSJani Nikula 
add_alternate_cea_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)443140f71f5bSJani Nikula static int add_alternate_cea_modes(struct drm_connector *connector,
443240f71f5bSJani Nikula 				   const struct drm_edid *drm_edid)
4433e6e79209SVille Syrjälä {
4434e6e79209SVille Syrjälä 	struct drm_device *dev = connector->dev;
4435e6e79209SVille Syrjälä 	struct drm_display_mode *mode, *tmp;
4436e6e79209SVille Syrjälä 	LIST_HEAD(list);
4437e6e79209SVille Syrjälä 	int modes = 0;
4438e6e79209SVille Syrjälä 
44396ff1c19fSJani Nikula 	/* Don't add CTA modes if the CTA extension block is missing */
444040f71f5bSJani Nikula 	if (!drm_edid_has_cta_extension(drm_edid))
4441e6e79209SVille Syrjälä 		return 0;
4442e6e79209SVille Syrjälä 
4443e6e79209SVille Syrjälä 	/*
4444e6e79209SVille Syrjälä 	 * Go through all probed modes and create a new mode
4445e6e79209SVille Syrjälä 	 * with the alternate clock for certain CEA modes.
4446e6e79209SVille Syrjälä 	 */
4447e6e79209SVille Syrjälä 	list_for_each_entry(mode, &connector->probed_modes, head) {
44483f2f6533SLespiau, Damien 		const struct drm_display_mode *cea_mode = NULL;
4449e6e79209SVille Syrjälä 		struct drm_display_mode *newmode;
4450d9278b4cSJani Nikula 		u8 vic = drm_match_cea_mode(mode);
4451e6e79209SVille Syrjälä 		unsigned int clock1, clock2;
4452e6e79209SVille Syrjälä 
4453d9278b4cSJani Nikula 		if (drm_valid_cea_vic(vic)) {
44547befe621SVille Syrjälä 			cea_mode = cea_mode_for_vic(vic);
44553f2f6533SLespiau, Damien 			clock2 = cea_mode_alternate_clock(cea_mode);
44563f2f6533SLespiau, Damien 		} else {
4457d9278b4cSJani Nikula 			vic = drm_match_hdmi_mode(mode);
4458d9278b4cSJani Nikula 			if (drm_valid_hdmi_vic(vic)) {
4459d9278b4cSJani Nikula 				cea_mode = &edid_4k_modes[vic];
44603f2f6533SLespiau, Damien 				clock2 = hdmi_mode_alternate_clock(cea_mode);
44613f2f6533SLespiau, Damien 			}
44623f2f6533SLespiau, Damien 		}
44633f2f6533SLespiau, Damien 
44643f2f6533SLespiau, Damien 		if (!cea_mode)
4465e6e79209SVille Syrjälä 			continue;
4466e6e79209SVille Syrjälä 
4467e6e79209SVille Syrjälä 		clock1 = cea_mode->clock;
4468e6e79209SVille Syrjälä 
4469e6e79209SVille Syrjälä 		if (clock1 == clock2)
4470e6e79209SVille Syrjälä 			continue;
4471e6e79209SVille Syrjälä 
4472e6e79209SVille Syrjälä 		if (mode->clock != clock1 && mode->clock != clock2)
4473e6e79209SVille Syrjälä 			continue;
4474e6e79209SVille Syrjälä 
4475e6e79209SVille Syrjälä 		newmode = drm_mode_duplicate(dev, cea_mode);
4476e6e79209SVille Syrjälä 		if (!newmode)
4477e6e79209SVille Syrjälä 			continue;
4478e6e79209SVille Syrjälä 
447927130212SDamien Lespiau 		/* Carry over the stereo flags */
448027130212SDamien Lespiau 		newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK;
448127130212SDamien Lespiau 
4482e6e79209SVille Syrjälä 		/*
4483e6e79209SVille Syrjälä 		 * The current mode could be either variant. Make
4484e6e79209SVille Syrjälä 		 * sure to pick the "other" clock for the new mode.
4485e6e79209SVille Syrjälä 		 */
4486e6e79209SVille Syrjälä 		if (mode->clock != clock1)
4487e6e79209SVille Syrjälä 			newmode->clock = clock1;
4488e6e79209SVille Syrjälä 		else
4489e6e79209SVille Syrjälä 			newmode->clock = clock2;
4490e6e79209SVille Syrjälä 
4491e6e79209SVille Syrjälä 		list_add_tail(&newmode->head, &list);
4492e6e79209SVille Syrjälä 	}
4493e6e79209SVille Syrjälä 
4494e6e79209SVille Syrjälä 	list_for_each_entry_safe(mode, tmp, &list, head) {
4495e6e79209SVille Syrjälä 		list_del(&mode->head);
4496e6e79209SVille Syrjälä 		drm_mode_probed_add(connector, mode);
4497e6e79209SVille Syrjälä 		modes++;
4498e6e79209SVille Syrjälä 	}
4499e6e79209SVille Syrjälä 
4500e6e79209SVille Syrjälä 	return modes;
4501e6e79209SVille Syrjälä }
4502a4799037SStephane Marchesin 
svd_to_vic(u8 svd)45038ec6e075SShashank Sharma static u8 svd_to_vic(u8 svd)
45048ec6e075SShashank Sharma {
45058ec6e075SShashank Sharma 	/* 0-6 bit vic, 7th bit native mode indicator */
45068ec6e075SShashank Sharma 	if ((svd >= 1 &&  svd <= 64) || (svd >= 129 && svd <= 192))
45078ec6e075SShashank Sharma 		return svd & 127;
45088ec6e075SShashank Sharma 
45098ec6e075SShashank Sharma 	return svd;
45108ec6e075SShashank Sharma }
45118ec6e075SShashank Sharma 
45126a40a75fSJani Nikula /*
45136a40a75fSJani Nikula  * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in
45146a40a75fSJani Nikula  * the EDID, or NULL on errors.
45156a40a75fSJani Nikula  */
4516aff04aceSThomas Wood static struct drm_display_mode *
drm_display_mode_from_vic_index(struct drm_connector * connector,int vic_index)45176a40a75fSJani Nikula drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index)
4518aff04aceSThomas Wood {
45196a40a75fSJani Nikula 	const struct drm_display_info *info = &connector->display_info;
4520aff04aceSThomas Wood 	struct drm_device *dev = connector->dev;
4521aff04aceSThomas Wood 
45226a40a75fSJani Nikula 	if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index])
4523aff04aceSThomas Wood 		return NULL;
4524aff04aceSThomas Wood 
45256a40a75fSJani Nikula 	return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]);
4526aff04aceSThomas Wood }
4527aff04aceSThomas Wood 
4528832d4f2fSShashank Sharma /*
4529832d4f2fSShashank Sharma  * do_y420vdb_modes - Parse YCBCR 420 only modes
4530832d4f2fSShashank Sharma  * @connector: connector corresponding to the HDMI sink
4531832d4f2fSShashank Sharma  * @svds: start of the data block of CEA YCBCR 420 VDB
4532832d4f2fSShashank Sharma  * @len: length of the CEA YCBCR 420 VDB
4533832d4f2fSShashank Sharma  *
4534832d4f2fSShashank Sharma  * Parse the CEA-861-F YCBCR 420 Video Data Block (Y420VDB)
4535832d4f2fSShashank Sharma  * which contains modes which can be supported in YCBCR 420
4536832d4f2fSShashank Sharma  * output format only.
4537832d4f2fSShashank Sharma  */
do_y420vdb_modes(struct drm_connector * connector,const u8 * svds,u8 svds_len)4538832d4f2fSShashank Sharma static int do_y420vdb_modes(struct drm_connector *connector,
4539832d4f2fSShashank Sharma 			    const u8 *svds, u8 svds_len)
4540832d4f2fSShashank Sharma {
4541832d4f2fSShashank Sharma 	struct drm_device *dev = connector->dev;
4542c54e2e23SJani Nikula 	int modes = 0, i;
4543832d4f2fSShashank Sharma 
4544832d4f2fSShashank Sharma 	for (i = 0; i < svds_len; i++) {
4545832d4f2fSShashank Sharma 		u8 vic = svd_to_vic(svds[i]);
4546832d4f2fSShashank Sharma 		struct drm_display_mode *newmode;
4547832d4f2fSShashank Sharma 
4548832d4f2fSShashank Sharma 		if (!drm_valid_cea_vic(vic))
4549832d4f2fSShashank Sharma 			continue;
4550832d4f2fSShashank Sharma 
45517befe621SVille Syrjälä 		newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic));
4552832d4f2fSShashank Sharma 		if (!newmode)
4553832d4f2fSShashank Sharma 			break;
4554832d4f2fSShashank Sharma 		drm_mode_probed_add(connector, newmode);
4555832d4f2fSShashank Sharma 		modes++;
4556832d4f2fSShashank Sharma 	}
4557832d4f2fSShashank Sharma 
4558832d4f2fSShashank Sharma 	return modes;
4559832d4f2fSShashank Sharma }
4560832d4f2fSShashank Sharma 
45617af655bcSVille Syrjälä /**
45627af655bcSVille Syrjälä  * drm_display_mode_from_cea_vic() - return a mode for CEA VIC
45637af655bcSVille Syrjälä  * @dev: DRM device
45648d7d8c0aSMauro Carvalho Chehab  * @video_code: CEA VIC of the mode
45657af655bcSVille Syrjälä  *
45667af655bcSVille Syrjälä  * Creates a new mode matching the specified CEA VIC.
45677af655bcSVille Syrjälä  *
45687af655bcSVille Syrjälä  * Returns: A new drm_display_mode on success or NULL on failure
45697af655bcSVille Syrjälä  */
45707af655bcSVille Syrjälä struct drm_display_mode *
drm_display_mode_from_cea_vic(struct drm_device * dev,u8 video_code)45717af655bcSVille Syrjälä drm_display_mode_from_cea_vic(struct drm_device *dev,
45727af655bcSVille Syrjälä 			      u8 video_code)
45737af655bcSVille Syrjälä {
45747af655bcSVille Syrjälä 	const struct drm_display_mode *cea_mode;
45757af655bcSVille Syrjälä 	struct drm_display_mode *newmode;
45767af655bcSVille Syrjälä 
45777af655bcSVille Syrjälä 	cea_mode = cea_mode_for_vic(video_code);
45787af655bcSVille Syrjälä 	if (!cea_mode)
45797af655bcSVille Syrjälä 		return NULL;
45807af655bcSVille Syrjälä 
45817af655bcSVille Syrjälä 	newmode = drm_mode_duplicate(dev, cea_mode);
45827af655bcSVille Syrjälä 	if (!newmode)
45837af655bcSVille Syrjälä 		return NULL;
45847af655bcSVille Syrjälä 
45857af655bcSVille Syrjälä 	return newmode;
45867af655bcSVille Syrjälä }
45877af655bcSVille Syrjälä EXPORT_SYMBOL(drm_display_mode_from_cea_vic);
45887af655bcSVille Syrjälä 
45896a40a75fSJani Nikula /* Add modes based on VICs parsed in parse_cta_vdb() */
add_cta_vdb_modes(struct drm_connector * connector)45906a40a75fSJani Nikula static int add_cta_vdb_modes(struct drm_connector *connector)
459154ac76f8SChristian Schmidt {
45926a40a75fSJani Nikula 	const struct drm_display_info *info = &connector->display_info;
4593aff04aceSThomas Wood 	int i, modes = 0;
459454ac76f8SChristian Schmidt 
45956a40a75fSJani Nikula 	if (!info->vics)
45966a40a75fSJani Nikula 		return 0;
45976a40a75fSJani Nikula 
45986a40a75fSJani Nikula 	for (i = 0; i < info->vics_len; i++) {
4599aff04aceSThomas Wood 		struct drm_display_mode *mode;
4600948de842SSuraj Upadhyay 
46016a40a75fSJani Nikula 		mode = drm_display_mode_from_vic_index(connector, i);
4602aff04aceSThomas Wood 		if (mode) {
4603aff04aceSThomas Wood 			drm_mode_probed_add(connector, mode);
460454ac76f8SChristian Schmidt 			modes++;
460554ac76f8SChristian Schmidt 		}
460654ac76f8SChristian Schmidt 	}
460754ac76f8SChristian Schmidt 
460854ac76f8SChristian Schmidt 	return modes;
460954ac76f8SChristian Schmidt }
461054ac76f8SChristian Schmidt 
4611c858cfcaSDamien Lespiau struct stereo_mandatory_mode {
4612c858cfcaSDamien Lespiau 	int width, height, vrefresh;
4613c858cfcaSDamien Lespiau 	unsigned int flags;
4614c858cfcaSDamien Lespiau };
4615c858cfcaSDamien Lespiau 
4616c858cfcaSDamien Lespiau static const struct stereo_mandatory_mode stereo_mandatory_modes[] = {
4617f7e121b7SDamien Lespiau 	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
4618f7e121b7SDamien Lespiau 	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING },
4619c858cfcaSDamien Lespiau 	{ 1920, 1080, 50,
4620c858cfcaSDamien Lespiau 	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
4621c858cfcaSDamien Lespiau 	{ 1920, 1080, 60,
4622c858cfcaSDamien Lespiau 	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
4623f7e121b7SDamien Lespiau 	{ 1280, 720,  50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
4624f7e121b7SDamien Lespiau 	{ 1280, 720,  50, DRM_MODE_FLAG_3D_FRAME_PACKING },
4625f7e121b7SDamien Lespiau 	{ 1280, 720,  60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
4626f7e121b7SDamien Lespiau 	{ 1280, 720,  60, DRM_MODE_FLAG_3D_FRAME_PACKING }
4627c858cfcaSDamien Lespiau };
4628c858cfcaSDamien Lespiau 
4629c858cfcaSDamien Lespiau static bool
stereo_match_mandatory(const struct drm_display_mode * mode,const struct stereo_mandatory_mode * stereo_mode)4630c858cfcaSDamien Lespiau stereo_match_mandatory(const struct drm_display_mode *mode,
4631c858cfcaSDamien Lespiau 		       const struct stereo_mandatory_mode *stereo_mode)
4632c858cfcaSDamien Lespiau {
4633c858cfcaSDamien Lespiau 	unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
4634c858cfcaSDamien Lespiau 
4635c858cfcaSDamien Lespiau 	return mode->hdisplay == stereo_mode->width &&
4636c858cfcaSDamien Lespiau 	       mode->vdisplay == stereo_mode->height &&
4637c858cfcaSDamien Lespiau 	       interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) &&
4638c858cfcaSDamien Lespiau 	       drm_mode_vrefresh(mode) == stereo_mode->vrefresh;
4639c858cfcaSDamien Lespiau }
4640c858cfcaSDamien Lespiau 
add_hdmi_mandatory_stereo_modes(struct drm_connector * connector)4641c858cfcaSDamien Lespiau static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector)
4642c858cfcaSDamien Lespiau {
4643c858cfcaSDamien Lespiau 	struct drm_device *dev = connector->dev;
4644c858cfcaSDamien Lespiau 	const struct drm_display_mode *mode;
4645c858cfcaSDamien Lespiau 	struct list_head stereo_modes;
4646f7e121b7SDamien Lespiau 	int modes = 0, i;
4647c858cfcaSDamien Lespiau 
4648c858cfcaSDamien Lespiau 	INIT_LIST_HEAD(&stereo_modes);
4649c858cfcaSDamien Lespiau 
4650c858cfcaSDamien Lespiau 	list_for_each_entry(mode, &connector->probed_modes, head) {
4651f7e121b7SDamien Lespiau 		for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) {
4652c858cfcaSDamien Lespiau 			const struct stereo_mandatory_mode *mandatory;
4653c858cfcaSDamien Lespiau 			struct drm_display_mode *new_mode;
4654c858cfcaSDamien Lespiau 
4655f7e121b7SDamien Lespiau 			if (!stereo_match_mandatory(mode,
4656f7e121b7SDamien Lespiau 						    &stereo_mandatory_modes[i]))
4657f7e121b7SDamien Lespiau 				continue;
4658c858cfcaSDamien Lespiau 
4659f7e121b7SDamien Lespiau 			mandatory = &stereo_mandatory_modes[i];
4660c858cfcaSDamien Lespiau 			new_mode = drm_mode_duplicate(dev, mode);
4661c858cfcaSDamien Lespiau 			if (!new_mode)
4662c858cfcaSDamien Lespiau 				continue;
4663c858cfcaSDamien Lespiau 
4664f7e121b7SDamien Lespiau 			new_mode->flags |= mandatory->flags;
4665c858cfcaSDamien Lespiau 			list_add_tail(&new_mode->head, &stereo_modes);
4666c858cfcaSDamien Lespiau 			modes++;
4667f7e121b7SDamien Lespiau 		}
4668c858cfcaSDamien Lespiau 	}
4669c858cfcaSDamien Lespiau 
4670c858cfcaSDamien Lespiau 	list_splice_tail(&stereo_modes, &connector->probed_modes);
4671c858cfcaSDamien Lespiau 
4672c858cfcaSDamien Lespiau 	return modes;
4673c858cfcaSDamien Lespiau }
4674c858cfcaSDamien Lespiau 
add_hdmi_mode(struct drm_connector * connector,u8 vic)46751deee8d7SDamien Lespiau static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
46761deee8d7SDamien Lespiau {
46771deee8d7SDamien Lespiau 	struct drm_device *dev = connector->dev;
46781deee8d7SDamien Lespiau 	struct drm_display_mode *newmode;
46791deee8d7SDamien Lespiau 
4680d9278b4cSJani Nikula 	if (!drm_valid_hdmi_vic(vic)) {
4681e1e7bc48SJani Nikula 		drm_err(connector->dev, "[CONNECTOR:%d:%s] Unknown HDMI VIC: %d\n",
4682e1e7bc48SJani Nikula 			connector->base.id, connector->name, vic);
46831deee8d7SDamien Lespiau 		return 0;
46841deee8d7SDamien Lespiau 	}
46851deee8d7SDamien Lespiau 
46861deee8d7SDamien Lespiau 	newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
46871deee8d7SDamien Lespiau 	if (!newmode)
46881deee8d7SDamien Lespiau 		return 0;
46891deee8d7SDamien Lespiau 
46901deee8d7SDamien Lespiau 	drm_mode_probed_add(connector, newmode);
46911deee8d7SDamien Lespiau 
46921deee8d7SDamien Lespiau 	return 1;
46931deee8d7SDamien Lespiau }
46941deee8d7SDamien Lespiau 
add_3d_struct_modes(struct drm_connector * connector,u16 structure,int vic_index)4695fbf46025SThomas Wood static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
46966a40a75fSJani Nikula 			       int vic_index)
4697fbf46025SThomas Wood {
4698fbf46025SThomas Wood 	struct drm_display_mode *newmode;
4699fbf46025SThomas Wood 	int modes = 0;
4700fbf46025SThomas Wood 
4701fbf46025SThomas Wood 	if (structure & (1 << 0)) {
47026a40a75fSJani Nikula 		newmode = drm_display_mode_from_vic_index(connector, vic_index);
4703fbf46025SThomas Wood 		if (newmode) {
4704fbf46025SThomas Wood 			newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
4705fbf46025SThomas Wood 			drm_mode_probed_add(connector, newmode);
4706fbf46025SThomas Wood 			modes++;
4707fbf46025SThomas Wood 		}
4708fbf46025SThomas Wood 	}
4709fbf46025SThomas Wood 	if (structure & (1 << 6)) {
47106a40a75fSJani Nikula 		newmode = drm_display_mode_from_vic_index(connector, vic_index);
4711fbf46025SThomas Wood 		if (newmode) {
4712fbf46025SThomas Wood 			newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
4713fbf46025SThomas Wood 			drm_mode_probed_add(connector, newmode);
4714fbf46025SThomas Wood 			modes++;
4715fbf46025SThomas Wood 		}
4716fbf46025SThomas Wood 	}
4717fbf46025SThomas Wood 	if (structure & (1 << 8)) {
47186a40a75fSJani Nikula 		newmode = drm_display_mode_from_vic_index(connector, vic_index);
4719fbf46025SThomas Wood 		if (newmode) {
472089570eebSThomas Wood 			newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
4721fbf46025SThomas Wood 			drm_mode_probed_add(connector, newmode);
4722fbf46025SThomas Wood 			modes++;
4723fbf46025SThomas Wood 		}
4724fbf46025SThomas Wood 	}
4725fbf46025SThomas Wood 
4726fbf46025SThomas Wood 	return modes;
4727fbf46025SThomas Wood }
4728fbf46025SThomas Wood 
hdmi_vsdb_latency_present(const u8 * db)47291ee3e217SJani Nikula static bool hdmi_vsdb_latency_present(const u8 *db)
47301ee3e217SJani Nikula {
47311ee3e217SJani Nikula 	return db[8] & BIT(7);
47321ee3e217SJani Nikula }
47331ee3e217SJani Nikula 
hdmi_vsdb_i_latency_present(const u8 * db)47341ee3e217SJani Nikula static bool hdmi_vsdb_i_latency_present(const u8 *db)
47351ee3e217SJani Nikula {
47361ee3e217SJani Nikula 	return hdmi_vsdb_latency_present(db) && db[8] & BIT(6);
47371ee3e217SJani Nikula }
47381ee3e217SJani Nikula 
hdmi_vsdb_latency_length(const u8 * db)4739cba83c1fSJani Nikula static int hdmi_vsdb_latency_length(const u8 *db)
4740cba83c1fSJani Nikula {
4741cba83c1fSJani Nikula 	if (hdmi_vsdb_i_latency_present(db))
4742cba83c1fSJani Nikula 		return 4;
4743cba83c1fSJani Nikula 	else if (hdmi_vsdb_latency_present(db))
4744cba83c1fSJani Nikula 		return 2;
4745cba83c1fSJani Nikula 	else
4746cba83c1fSJani Nikula 		return 0;
4747cba83c1fSJani Nikula }
4748cba83c1fSJani Nikula 
47497ebe1963SLespiau, Damien /*
47507ebe1963SLespiau, Damien  * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
47517ebe1963SLespiau, Damien  * @connector: connector corresponding to the HDMI sink
47527ebe1963SLespiau, Damien  * @db: start of the CEA vendor specific block
47537ebe1963SLespiau, Damien  * @len: length of the CEA block payload, ie. one can access up to db[len]
47547ebe1963SLespiau, Damien  *
4755c858cfcaSDamien Lespiau  * Parses the HDMI VSDB looking for modes to add to @connector. This function
4756c858cfcaSDamien Lespiau  * also adds the stereo 3d modes when applicable.
47577ebe1963SLespiau, Damien  */
47587ebe1963SLespiau, Damien static int
do_hdmi_vsdb_modes(struct drm_connector * connector,const u8 * db,u8 len)47596a40a75fSJani Nikula do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
47607ebe1963SLespiau, Damien {
47610e5083aaSThomas Wood 	int modes = 0, offset = 0, i, multi_present = 0, multi_len;
4762fbf46025SThomas Wood 	u8 vic_len, hdmi_3d_len = 0;
4763fbf46025SThomas Wood 	u16 mask;
4764fbf46025SThomas Wood 	u16 structure_all;
47657ebe1963SLespiau, Damien 
47667ebe1963SLespiau, Damien 	if (len < 8)
47677ebe1963SLespiau, Damien 		goto out;
47687ebe1963SLespiau, Damien 
47697ebe1963SLespiau, Damien 	/* no HDMI_Video_Present */
47707ebe1963SLespiau, Damien 	if (!(db[8] & (1 << 5)))
47717ebe1963SLespiau, Damien 		goto out;
47727ebe1963SLespiau, Damien 
4773cba83c1fSJani Nikula 	offset += hdmi_vsdb_latency_length(db);
47747ebe1963SLespiau, Damien 
47757ebe1963SLespiau, Damien 	/* the declared length is not long enough for the 2 first bytes
47767ebe1963SLespiau, Damien 	 * of additional video format capabilities */
4777c858cfcaSDamien Lespiau 	if (len < (8 + offset + 2))
47787ebe1963SLespiau, Damien 		goto out;
47797ebe1963SLespiau, Damien 
4780c858cfcaSDamien Lespiau 	/* 3D_Present */
4781c858cfcaSDamien Lespiau 	offset++;
4782fbf46025SThomas Wood 	if (db[8 + offset] & (1 << 7)) {
4783c858cfcaSDamien Lespiau 		modes += add_hdmi_mandatory_stereo_modes(connector);
4784c858cfcaSDamien Lespiau 
4785fbf46025SThomas Wood 		/* 3D_Multi_present */
4786fbf46025SThomas Wood 		multi_present = (db[8 + offset] & 0x60) >> 5;
4787fbf46025SThomas Wood 	}
4788fbf46025SThomas Wood 
4789c858cfcaSDamien Lespiau 	offset++;
47907ebe1963SLespiau, Damien 	vic_len = db[8 + offset] >> 5;
4791fbf46025SThomas Wood 	hdmi_3d_len = db[8 + offset] & 0x1f;
47927ebe1963SLespiau, Damien 
47937ebe1963SLespiau, Damien 	for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
47947ebe1963SLespiau, Damien 		u8 vic;
47957ebe1963SLespiau, Damien 
47967ebe1963SLespiau, Damien 		vic = db[9 + offset + i];
47971deee8d7SDamien Lespiau 		modes += add_hdmi_mode(connector, vic);
47987ebe1963SLespiau, Damien 	}
4799fbf46025SThomas Wood 	offset += 1 + vic_len;
4800fbf46025SThomas Wood 
48010e5083aaSThomas Wood 	if (multi_present == 1)
48020e5083aaSThomas Wood 		multi_len = 2;
48030e5083aaSThomas Wood 	else if (multi_present == 2)
48040e5083aaSThomas Wood 		multi_len = 4;
48050e5083aaSThomas Wood 	else
48060e5083aaSThomas Wood 		multi_len = 0;
48070e5083aaSThomas Wood 
48080e5083aaSThomas Wood 	if (len < (8 + offset + hdmi_3d_len - 1))
4809fbf46025SThomas Wood 		goto out;
4810fbf46025SThomas Wood 
48110e5083aaSThomas Wood 	if (hdmi_3d_len < multi_len)
4812fbf46025SThomas Wood 		goto out;
4813fbf46025SThomas Wood 
48140e5083aaSThomas Wood 	if (multi_present == 1 || multi_present == 2) {
4815fbf46025SThomas Wood 		/* 3D_Structure_ALL */
4816fbf46025SThomas Wood 		structure_all = (db[8 + offset] << 8) | db[9 + offset];
4817fbf46025SThomas Wood 
4818fbf46025SThomas Wood 		/* check if 3D_MASK is present */
4819fbf46025SThomas Wood 		if (multi_present == 2)
4820fbf46025SThomas Wood 			mask = (db[10 + offset] << 8) | db[11 + offset];
4821fbf46025SThomas Wood 		else
4822fbf46025SThomas Wood 			mask = 0xffff;
4823fbf46025SThomas Wood 
4824fbf46025SThomas Wood 		for (i = 0; i < 16; i++) {
4825fbf46025SThomas Wood 			if (mask & (1 << i))
4826fbf46025SThomas Wood 				modes += add_3d_struct_modes(connector,
48276a40a75fSJani Nikula 							     structure_all, i);
4828fbf46025SThomas Wood 		}
48290e5083aaSThomas Wood 	}
48300e5083aaSThomas Wood 
48310e5083aaSThomas Wood 	offset += multi_len;
48320e5083aaSThomas Wood 
48330e5083aaSThomas Wood 	for (i = 0; i < (hdmi_3d_len - multi_len); i++) {
48340e5083aaSThomas Wood 		int vic_index;
48350e5083aaSThomas Wood 		struct drm_display_mode *newmode = NULL;
48360e5083aaSThomas Wood 		unsigned int newflag = 0;
48370e5083aaSThomas Wood 		bool detail_present;
48380e5083aaSThomas Wood 
48390e5083aaSThomas Wood 		detail_present = ((db[8 + offset + i] & 0x0f) > 7);
48400e5083aaSThomas Wood 
48410e5083aaSThomas Wood 		if (detail_present && (i + 1 == hdmi_3d_len - multi_len))
48420e5083aaSThomas Wood 			break;
48430e5083aaSThomas Wood 
48440e5083aaSThomas Wood 		/* 2D_VIC_order_X */
48450e5083aaSThomas Wood 		vic_index = db[8 + offset + i] >> 4;
48460e5083aaSThomas Wood 
48470e5083aaSThomas Wood 		/* 3D_Structure_X */
48480e5083aaSThomas Wood 		switch (db[8 + offset + i] & 0x0f) {
48490e5083aaSThomas Wood 		case 0:
48500e5083aaSThomas Wood 			newflag = DRM_MODE_FLAG_3D_FRAME_PACKING;
48510e5083aaSThomas Wood 			break;
48520e5083aaSThomas Wood 		case 6:
48530e5083aaSThomas Wood 			newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
48540e5083aaSThomas Wood 			break;
48550e5083aaSThomas Wood 		case 8:
48560e5083aaSThomas Wood 			/* 3D_Detail_X */
48570e5083aaSThomas Wood 			if ((db[9 + offset + i] >> 4) == 1)
48580e5083aaSThomas Wood 				newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
48590e5083aaSThomas Wood 			break;
48600e5083aaSThomas Wood 		}
48610e5083aaSThomas Wood 
48620e5083aaSThomas Wood 		if (newflag != 0) {
48630e5083aaSThomas Wood 			newmode = drm_display_mode_from_vic_index(connector,
48640e5083aaSThomas Wood 								  vic_index);
48650e5083aaSThomas Wood 
48660e5083aaSThomas Wood 			if (newmode) {
48670e5083aaSThomas Wood 				newmode->flags |= newflag;
48680e5083aaSThomas Wood 				drm_mode_probed_add(connector, newmode);
48690e5083aaSThomas Wood 				modes++;
48700e5083aaSThomas Wood 			}
48710e5083aaSThomas Wood 		}
48720e5083aaSThomas Wood 
48730e5083aaSThomas Wood 		if (detail_present)
48740e5083aaSThomas Wood 			i++;
48750e5083aaSThomas Wood 	}
48767ebe1963SLespiau, Damien 
48777ebe1963SLespiau, Damien out:
48787ebe1963SLespiau, Damien 	return modes;
48797ebe1963SLespiau, Damien }
48807ebe1963SLespiau, Damien 
488154ac76f8SChristian Schmidt static int
cea_revision(const u8 * cea)48829e50b9d5SVille Syrjälä cea_revision(const u8 *cea)
48839e50b9d5SVille Syrjälä {
48845036c0d0SVille Syrjälä 	/*
48855036c0d0SVille Syrjälä 	 * FIXME is this correct for the DispID variant?
48865036c0d0SVille Syrjälä 	 * The DispID spec doesn't really specify whether
48875036c0d0SVille Syrjälä 	 * this is the revision of the CEA extension or
48885036c0d0SVille Syrjälä 	 * the DispID CEA data block. And the only value
48895036c0d0SVille Syrjälä 	 * given as an example is 0.
48905036c0d0SVille Syrjälä 	 */
48919e50b9d5SVille Syrjälä 	return cea[1];
48929e50b9d5SVille Syrjälä }
48939e50b9d5SVille Syrjälä 
4894aba58254SJani Nikula /*
4895aba58254SJani Nikula  * CTA Data Block iterator.
4896aba58254SJani Nikula  *
4897aba58254SJani Nikula  * Iterate through all CTA Data Blocks in both EDID CTA Extensions and DisplayID
4898aba58254SJani Nikula  * CTA Data Blocks.
4899aba58254SJani Nikula  *
4900aba58254SJani Nikula  * struct cea_db *db:
4901aba58254SJani Nikula  * struct cea_db_iter iter;
4902aba58254SJani Nikula  *
4903aba58254SJani Nikula  * cea_db_iter_edid_begin(edid, &iter);
4904aba58254SJani Nikula  * cea_db_iter_for_each(db, &iter) {
4905aba58254SJani Nikula  *         // do stuff with db
4906aba58254SJani Nikula  * }
4907aba58254SJani Nikula  * cea_db_iter_end(&iter);
4908aba58254SJani Nikula  */
4909aba58254SJani Nikula struct cea_db_iter {
4910aba58254SJani Nikula 	struct drm_edid_iter edid_iter;
4911aba58254SJani Nikula 	struct displayid_iter displayid_iter;
4912aba58254SJani Nikula 
4913aba58254SJani Nikula 	/* Current Data Block Collection. */
4914aba58254SJani Nikula 	const u8 *collection;
4915aba58254SJani Nikula 
4916aba58254SJani Nikula 	/* Current Data Block index in current collection. */
4917aba58254SJani Nikula 	int index;
4918aba58254SJani Nikula 
4919aba58254SJani Nikula 	/* End index in current collection. */
4920aba58254SJani Nikula 	int end;
4921aba58254SJani Nikula };
4922aba58254SJani Nikula 
4923aba58254SJani Nikula /* CTA-861-H section 7.4 CTA Data BLock Collection */
4924aba58254SJani Nikula struct cea_db {
4925aba58254SJani Nikula 	u8 tag_length;
4926aba58254SJani Nikula 	u8 data[];
4927aba58254SJani Nikula } __packed;
4928aba58254SJani Nikula 
cea_db_tag(const struct cea_db * db)492949a62a29SJani Nikula static int cea_db_tag(const struct cea_db *db)
4930aba58254SJani Nikula {
4931aba58254SJani Nikula 	return db->tag_length >> 5;
4932aba58254SJani Nikula }
4933aba58254SJani Nikula 
cea_db_payload_len(const void * _db)4934aba58254SJani Nikula static int cea_db_payload_len(const void *_db)
4935aba58254SJani Nikula {
4936aba58254SJani Nikula 	/* FIXME: Transition to passing struct cea_db * everywhere. */
4937aba58254SJani Nikula 	const struct cea_db *db = _db;
4938aba58254SJani Nikula 
4939aba58254SJani Nikula 	return db->tag_length & 0x1f;
4940aba58254SJani Nikula }
4941aba58254SJani Nikula 
cea_db_data(const struct cea_db * db)4942aba58254SJani Nikula static const void *cea_db_data(const struct cea_db *db)
4943aba58254SJani Nikula {
4944aba58254SJani Nikula 	return db->data;
4945aba58254SJani Nikula }
4946aba58254SJani Nikula 
cea_db_is_extended_tag(const struct cea_db * db,int tag)4947a9ec4fd0SJani Nikula static bool cea_db_is_extended_tag(const struct cea_db *db, int tag)
4948a9ec4fd0SJani Nikula {
4949a9ec4fd0SJani Nikula 	return cea_db_tag(db) == CTA_DB_EXTENDED_TAG &&
4950a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 1 &&
4951a9ec4fd0SJani Nikula 		db->data[0] == tag;
4952a9ec4fd0SJani Nikula }
4953a9ec4fd0SJani Nikula 
cea_db_is_vendor(const struct cea_db * db,int vendor_oui)4954a9ec4fd0SJani Nikula static bool cea_db_is_vendor(const struct cea_db *db, int vendor_oui)
4955a9ec4fd0SJani Nikula {
4956a9ec4fd0SJani Nikula 	const u8 *data = cea_db_data(db);
4957a9ec4fd0SJani Nikula 
4958a9ec4fd0SJani Nikula 	return cea_db_tag(db) == CTA_DB_VENDOR &&
4959a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 3 &&
4960a9ec4fd0SJani Nikula 		oui(data[2], data[1], data[0]) == vendor_oui;
4961a9ec4fd0SJani Nikula }
4962a9ec4fd0SJani Nikula 
cea_db_iter_edid_begin(const struct drm_edid * drm_edid,struct cea_db_iter * iter)49635e87b2e5SJani Nikula static void cea_db_iter_edid_begin(const struct drm_edid *drm_edid,
49645e87b2e5SJani Nikula 				   struct cea_db_iter *iter)
4965aba58254SJani Nikula {
4966aba58254SJani Nikula 	memset(iter, 0, sizeof(*iter));
4967aba58254SJani Nikula 
4968bbded689SJani Nikula 	drm_edid_iter_begin(drm_edid, &iter->edid_iter);
4969d9ba1b4cSJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter->displayid_iter);
4970aba58254SJani Nikula }
4971aba58254SJani Nikula 
4972aba58254SJani Nikula static const struct cea_db *
__cea_db_iter_current_block(const struct cea_db_iter * iter)4973aba58254SJani Nikula __cea_db_iter_current_block(const struct cea_db_iter *iter)
4974aba58254SJani Nikula {
4975aba58254SJani Nikula 	const struct cea_db *db;
4976aba58254SJani Nikula 
4977aba58254SJani Nikula 	if (!iter->collection)
4978aba58254SJani Nikula 		return NULL;
4979aba58254SJani Nikula 
4980aba58254SJani Nikula 	db = (const struct cea_db *)&iter->collection[iter->index];
4981aba58254SJani Nikula 
4982aba58254SJani Nikula 	if (iter->index + sizeof(*db) <= iter->end &&
4983aba58254SJani Nikula 	    iter->index + sizeof(*db) + cea_db_payload_len(db) <= iter->end)
4984aba58254SJani Nikula 		return db;
4985aba58254SJani Nikula 
4986aba58254SJani Nikula 	return NULL;
4987aba58254SJani Nikula }
4988aba58254SJani Nikula 
4989aba58254SJani Nikula /*
4990aba58254SJani Nikula  * References:
499111a8d095SJani Nikula  * - CTA-861-H section 7.3.3 CTA Extension Version 3
499211a8d095SJani Nikula  */
cea_db_collection_size(const u8 * cta)499311a8d095SJani Nikula static int cea_db_collection_size(const u8 *cta)
499411a8d095SJani Nikula {
499511a8d095SJani Nikula 	u8 d = cta[2];
499611a8d095SJani Nikula 
499711a8d095SJani Nikula 	if (d < 4 || d > 127)
499811a8d095SJani Nikula 		return 0;
499911a8d095SJani Nikula 
500011a8d095SJani Nikula 	return d - 4;
500111a8d095SJani Nikula }
500211a8d095SJani Nikula 
500311a8d095SJani Nikula /*
500411a8d095SJani Nikula  * References:
5005aba58254SJani Nikula  * - VESA E-EDID v1.4
5006aba58254SJani Nikula  * - CTA-861-H section 7.3.3 CTA Extension Version 3
5007aba58254SJani Nikula  */
__cea_db_iter_edid_next(struct cea_db_iter * iter)5008aba58254SJani Nikula static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter)
5009aba58254SJani Nikula {
5010aba58254SJani Nikula 	const u8 *ext;
5011aba58254SJani Nikula 
5012aba58254SJani Nikula 	drm_edid_iter_for_each(ext, &iter->edid_iter) {
501311a8d095SJani Nikula 		int size;
501411a8d095SJani Nikula 
5015aba58254SJani Nikula 		/* Only support CTA Extension revision 3+ */
5016aba58254SJani Nikula 		if (ext[0] != CEA_EXT || cea_revision(ext) < 3)
5017aba58254SJani Nikula 			continue;
5018aba58254SJani Nikula 
501911a8d095SJani Nikula 		size = cea_db_collection_size(ext);
502011a8d095SJani Nikula 		if (!size)
5021aba58254SJani Nikula 			continue;
5022aba58254SJani Nikula 
502311a8d095SJani Nikula 		iter->index = 4;
502411a8d095SJani Nikula 		iter->end = iter->index + size;
502511a8d095SJani Nikula 
5026aba58254SJani Nikula 		return ext;
5027aba58254SJani Nikula 	}
5028aba58254SJani Nikula 
5029aba58254SJani Nikula 	return NULL;
5030aba58254SJani Nikula }
5031aba58254SJani Nikula 
5032aba58254SJani Nikula /*
5033aba58254SJani Nikula  * References:
5034aba58254SJani Nikula  * - DisplayID v1.3 Appendix C: CEA Data Block within a DisplayID Data Block
5035aba58254SJani Nikula  * - DisplayID v2.0 section 4.10 CTA DisplayID Data Block
5036aba58254SJani Nikula  *
5037aba58254SJani Nikula  * Note that the above do not specify any connection between DisplayID Data
5038aba58254SJani Nikula  * Block revision and CTA Extension versions.
5039aba58254SJani Nikula  */
__cea_db_iter_displayid_next(struct cea_db_iter * iter)5040aba58254SJani Nikula static const void *__cea_db_iter_displayid_next(struct cea_db_iter *iter)
5041aba58254SJani Nikula {
5042aba58254SJani Nikula 	const struct displayid_block *block;
5043aba58254SJani Nikula 
5044aba58254SJani Nikula 	displayid_iter_for_each(block, &iter->displayid_iter) {
5045aba58254SJani Nikula 		if (block->tag != DATA_BLOCK_CTA)
5046aba58254SJani Nikula 			continue;
5047aba58254SJani Nikula 
5048aba58254SJani Nikula 		/*
5049aba58254SJani Nikula 		 * The displayid iterator has already verified the block bounds
5050aba58254SJani Nikula 		 * in displayid_iter_block().
5051aba58254SJani Nikula 		 */
5052aba58254SJani Nikula 		iter->index = sizeof(*block);
5053aba58254SJani Nikula 		iter->end = iter->index + block->num_bytes;
5054aba58254SJani Nikula 
5055aba58254SJani Nikula 		return block;
5056aba58254SJani Nikula 	}
5057aba58254SJani Nikula 
5058aba58254SJani Nikula 	return NULL;
5059aba58254SJani Nikula }
5060aba58254SJani Nikula 
__cea_db_iter_next(struct cea_db_iter * iter)5061aba58254SJani Nikula static const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter)
5062aba58254SJani Nikula {
5063aba58254SJani Nikula 	const struct cea_db *db;
5064aba58254SJani Nikula 
5065aba58254SJani Nikula 	if (iter->collection) {
5066aba58254SJani Nikula 		/* Current collection should always be valid. */
5067aba58254SJani Nikula 		db = __cea_db_iter_current_block(iter);
5068aba58254SJani Nikula 		if (WARN_ON(!db)) {
5069aba58254SJani Nikula 			iter->collection = NULL;
5070aba58254SJani Nikula 			return NULL;
5071aba58254SJani Nikula 		}
5072aba58254SJani Nikula 
5073aba58254SJani Nikula 		/* Next block in CTA Data Block Collection */
5074aba58254SJani Nikula 		iter->index += sizeof(*db) + cea_db_payload_len(db);
5075aba58254SJani Nikula 
5076aba58254SJani Nikula 		db = __cea_db_iter_current_block(iter);
5077aba58254SJani Nikula 		if (db)
5078aba58254SJani Nikula 			return db;
5079aba58254SJani Nikula 	}
5080aba58254SJani Nikula 
5081aba58254SJani Nikula 	for (;;) {
5082aba58254SJani Nikula 		/*
5083aba58254SJani Nikula 		 * Find the next CTA Data Block Collection. First iterate all
5084aba58254SJani Nikula 		 * the EDID CTA Extensions, then all the DisplayID CTA blocks.
5085aba58254SJani Nikula 		 *
5086aba58254SJani Nikula 		 * Per DisplayID v1.3 Appendix B: DisplayID as an EDID
5087aba58254SJani Nikula 		 * Extension, it's recommended that DisplayID extensions are
5088aba58254SJani Nikula 		 * exposed after all of the CTA Extensions.
5089aba58254SJani Nikula 		 */
5090aba58254SJani Nikula 		iter->collection = __cea_db_iter_edid_next(iter);
5091aba58254SJani Nikula 		if (!iter->collection)
5092aba58254SJani Nikula 			iter->collection = __cea_db_iter_displayid_next(iter);
5093aba58254SJani Nikula 
5094aba58254SJani Nikula 		if (!iter->collection)
5095aba58254SJani Nikula 			return NULL;
5096aba58254SJani Nikula 
5097aba58254SJani Nikula 		db = __cea_db_iter_current_block(iter);
5098aba58254SJani Nikula 		if (db)
5099aba58254SJani Nikula 			return db;
5100aba58254SJani Nikula 	}
5101aba58254SJani Nikula }
5102aba58254SJani Nikula 
5103aba58254SJani Nikula #define cea_db_iter_for_each(__db, __iter) \
5104aba58254SJani Nikula 	while (((__db) = __cea_db_iter_next(__iter)))
5105aba58254SJani Nikula 
cea_db_iter_end(struct cea_db_iter * iter)5106aba58254SJani Nikula static void cea_db_iter_end(struct cea_db_iter *iter)
5107aba58254SJani Nikula {
5108aba58254SJani Nikula 	displayid_iter_end(&iter->displayid_iter);
5109aba58254SJani Nikula 	drm_edid_iter_end(&iter->edid_iter);
5110aba58254SJani Nikula 
5111aba58254SJani Nikula 	memset(iter, 0, sizeof(*iter));
5112aba58254SJani Nikula }
5113aba58254SJani Nikula 
cea_db_is_hdmi_vsdb(const struct cea_db * db)511449a62a29SJani Nikula static bool cea_db_is_hdmi_vsdb(const struct cea_db *db)
51157ebe1963SLespiau, Damien {
5116a9ec4fd0SJani Nikula 	return cea_db_is_vendor(db, HDMI_IEEE_OUI) &&
5117a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 5;
51187ebe1963SLespiau, Damien }
51197ebe1963SLespiau, Damien 
cea_db_is_hdmi_forum_vsdb(const struct cea_db * db)512049a62a29SJani Nikula static bool cea_db_is_hdmi_forum_vsdb(const struct cea_db *db)
512150dd1bd1SThierry Reding {
5122a9ec4fd0SJani Nikula 	return cea_db_is_vendor(db, HDMI_FORUM_IEEE_OUI) &&
5123a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 7;
512450dd1bd1SThierry Reding }
512550dd1bd1SThierry Reding 
cea_db_is_hdmi_forum_eeodb(const void * db)512618e3c1d5SJani Nikula static bool cea_db_is_hdmi_forum_eeodb(const void *db)
512718e3c1d5SJani Nikula {
512818e3c1d5SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_EEODB) &&
512918e3c1d5SJani Nikula 		cea_db_payload_len(db) >= 2;
513018e3c1d5SJani Nikula }
513118e3c1d5SJani Nikula 
cea_db_is_microsoft_vsdb(const struct cea_db * db)513249a62a29SJani Nikula static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
51332869f599SPhilipp Zabel {
5134a9ec4fd0SJani Nikula 	return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) &&
5135a9ec4fd0SJani Nikula 		cea_db_payload_len(db) == 21;
51362869f599SPhilipp Zabel }
51372869f599SPhilipp Zabel 
cea_db_is_vcdb(const struct cea_db * db)513849a62a29SJani Nikula static bool cea_db_is_vcdb(const struct cea_db *db)
51391581b2dfSVille Syrjälä {
5140a9ec4fd0SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
5141a9ec4fd0SJani Nikula 		cea_db_payload_len(db) == 2;
51421581b2dfSVille Syrjälä }
51431581b2dfSVille Syrjälä 
cea_db_is_hdmi_forum_scdb(const struct cea_db * db)514449a62a29SJani Nikula static bool cea_db_is_hdmi_forum_scdb(const struct cea_db *db)
5145115fcf58SLee Shawn C {
5146a9ec4fd0SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_SCDB) &&
5147a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 7;
5148115fcf58SLee Shawn C }
5149115fcf58SLee Shawn C 
cea_db_is_y420cmdb(const struct cea_db * db)515049a62a29SJani Nikula static bool cea_db_is_y420cmdb(const struct cea_db *db)
5151832d4f2fSShashank Sharma {
5152a9ec4fd0SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_CAP_MAP);
5153832d4f2fSShashank Sharma }
5154832d4f2fSShashank Sharma 
cea_db_is_y420vdb(const struct cea_db * db)515549a62a29SJani Nikula static bool cea_db_is_y420vdb(const struct cea_db *db)
5156832d4f2fSShashank Sharma {
5157a9ec4fd0SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_DATA);
5158a9ec4fd0SJani Nikula }
5159832d4f2fSShashank Sharma 
cea_db_is_hdmi_hdr_metadata_block(const struct cea_db * db)516049a62a29SJani Nikula static bool cea_db_is_hdmi_hdr_metadata_block(const struct cea_db *db)
5161a9ec4fd0SJani Nikula {
5162a9ec4fd0SJani Nikula 	return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) &&
5163a9ec4fd0SJani Nikula 		cea_db_payload_len(db) >= 3;
5164832d4f2fSShashank Sharma }
5165832d4f2fSShashank Sharma 
516618e3c1d5SJani Nikula /*
516718e3c1d5SJani Nikula  * Get the HF-EEODB override extension block count from EDID.
516818e3c1d5SJani Nikula  *
516918e3c1d5SJani Nikula  * The passed in EDID may be partially read, as long as it has at least two
517018e3c1d5SJani Nikula  * blocks (base block and one extension block) if EDID extension count is > 0.
517118e3c1d5SJani Nikula  *
517218e3c1d5SJani Nikula  * Note that this is *not* how you should parse CTA Data Blocks in general; this
517318e3c1d5SJani Nikula  * is only to handle partially read EDIDs. Normally, use the CTA Data Block
517418e3c1d5SJani Nikula  * iterators instead.
517518e3c1d5SJani Nikula  *
517618e3c1d5SJani Nikula  * References:
517718e3c1d5SJani Nikula  * - HDMI 2.1 section 10.3.6 HDMI Forum EDID Extension Override Data Block
517818e3c1d5SJani Nikula  */
edid_hfeeodb_extension_block_count(const struct edid * edid)517918e3c1d5SJani Nikula static int edid_hfeeodb_extension_block_count(const struct edid *edid)
518018e3c1d5SJani Nikula {
518118e3c1d5SJani Nikula 	const u8 *cta;
518218e3c1d5SJani Nikula 
518318e3c1d5SJani Nikula 	/* No extensions according to base block, no HF-EEODB. */
518418e3c1d5SJani Nikula 	if (!edid_extension_block_count(edid))
518518e3c1d5SJani Nikula 		return 0;
518618e3c1d5SJani Nikula 
518718e3c1d5SJani Nikula 	/* HF-EEODB is always in the first EDID extension block only */
518818e3c1d5SJani Nikula 	cta = edid_extension_block_data(edid, 0);
518918e3c1d5SJani Nikula 	if (edid_block_tag(cta) != CEA_EXT || cea_revision(cta) < 3)
519018e3c1d5SJani Nikula 		return 0;
519118e3c1d5SJani Nikula 
519218e3c1d5SJani Nikula 	/* Need to have the data block collection, and at least 3 bytes. */
519318e3c1d5SJani Nikula 	if (cea_db_collection_size(cta) < 3)
519418e3c1d5SJani Nikula 		return 0;
519518e3c1d5SJani Nikula 
519618e3c1d5SJani Nikula 	/*
519718e3c1d5SJani Nikula 	 * Sinks that include the HF-EEODB in their E-EDID shall include one and
519818e3c1d5SJani Nikula 	 * only one instance of the HF-EEODB in the E-EDID, occupying bytes 4
519918e3c1d5SJani Nikula 	 * through 6 of Block 1 of the E-EDID.
520018e3c1d5SJani Nikula 	 */
520118e3c1d5SJani Nikula 	if (!cea_db_is_hdmi_forum_eeodb(&cta[4]))
520218e3c1d5SJani Nikula 		return 0;
520318e3c1d5SJani Nikula 
520418e3c1d5SJani Nikula 	return cta[4 + 2];
520518e3c1d5SJani Nikula }
520618e3c1d5SJani Nikula 
520761e05fdcSJani Nikula /*
520861e05fdcSJani Nikula  * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB)
520961e05fdcSJani Nikula  *
521061e05fdcSJani Nikula  * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB,
521161e05fdcSJani Nikula  * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444
521261e05fdcSJani Nikula  * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can
521361e05fdcSJani Nikula  * support YCBCR420 output too.
521461e05fdcSJani Nikula  */
parse_cta_y420cmdb(struct drm_connector * connector,const struct cea_db * db,u64 * y420cmdb_map)521561e05fdcSJani Nikula static void parse_cta_y420cmdb(struct drm_connector *connector,
521661e05fdcSJani Nikula 			       const struct cea_db *db, u64 *y420cmdb_map)
5217832d4f2fSShashank Sharma {
5218832d4f2fSShashank Sharma 	struct drm_display_info *info = &connector->display_info;
521961e05fdcSJani Nikula 	int i, map_len = cea_db_payload_len(db) - 1;
522061e05fdcSJani Nikula 	const u8 *data = cea_db_data(db) + 1;
5221832d4f2fSShashank Sharma 	u64 map = 0;
5222832d4f2fSShashank Sharma 
5223832d4f2fSShashank Sharma 	if (map_len == 0) {
5224832d4f2fSShashank Sharma 		/* All CEA modes support ycbcr420 sampling also.*/
522561e05fdcSJani Nikula 		map = U64_MAX;
522661e05fdcSJani Nikula 		goto out;
5227832d4f2fSShashank Sharma 	}
5228832d4f2fSShashank Sharma 
5229832d4f2fSShashank Sharma 	/*
5230832d4f2fSShashank Sharma 	 * This map indicates which of the existing CEA block modes
5231832d4f2fSShashank Sharma 	 * from VDB can support YCBCR420 output too. So if bit=0 is
5232832d4f2fSShashank Sharma 	 * set, first mode from VDB can support YCBCR420 output too.
5233832d4f2fSShashank Sharma 	 * We will parse and keep this map, before parsing VDB itself
5234832d4f2fSShashank Sharma 	 * to avoid going through the same block again and again.
5235832d4f2fSShashank Sharma 	 *
5236832d4f2fSShashank Sharma 	 * Spec is not clear about max possible size of this block.
5237832d4f2fSShashank Sharma 	 * Clamping max bitmap block size at 8 bytes. Every byte can
5238832d4f2fSShashank Sharma 	 * address 8 CEA modes, in this way this map can address
5239832d4f2fSShashank Sharma 	 * 8*8 = first 64 SVDs.
5240832d4f2fSShashank Sharma 	 */
5241832d4f2fSShashank Sharma 	if (WARN_ON_ONCE(map_len > 8))
5242832d4f2fSShashank Sharma 		map_len = 8;
5243832d4f2fSShashank Sharma 
524461e05fdcSJani Nikula 	for (i = 0; i < map_len; i++)
524561e05fdcSJani Nikula 		map |= (u64)data[i] << (8 * i);
5246832d4f2fSShashank Sharma 
524761e05fdcSJani Nikula out:
5248832d4f2fSShashank Sharma 	if (map)
5249c03d0b52SMaxime Ripard 		info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
5250832d4f2fSShashank Sharma 
525161e05fdcSJani Nikula 	*y420cmdb_map = map;
5252832d4f2fSShashank Sharma }
5253832d4f2fSShashank Sharma 
add_cea_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)525440f71f5bSJani Nikula static int add_cea_modes(struct drm_connector *connector,
525540f71f5bSJani Nikula 			 const struct drm_edid *drm_edid)
525654ac76f8SChristian Schmidt {
5257537d9ed2SJani Nikula 	const struct cea_db *db;
5258537d9ed2SJani Nikula 	struct cea_db_iter iter;
52596a40a75fSJani Nikula 	int modes;
52606a40a75fSJani Nikula 
52616a40a75fSJani Nikula 	/* CTA VDB block VICs parsed earlier */
52626a40a75fSJani Nikula 	modes = add_cta_vdb_modes(connector);
526354ac76f8SChristian Schmidt 
52645e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
5265537d9ed2SJani Nikula 	cea_db_iter_for_each(db, &iter) {
52666a40a75fSJani Nikula 		if (cea_db_is_hdmi_vsdb(db)) {
52676a40a75fSJani Nikula 			modes += do_hdmi_vsdb_modes(connector, (const u8 *)db,
52686a40a75fSJani Nikula 						    cea_db_payload_len(db));
5269832d4f2fSShashank Sharma 		} else if (cea_db_is_y420vdb(db)) {
5270537d9ed2SJani Nikula 			const u8 *vdb420 = cea_db_data(db) + 1;
5271832d4f2fSShashank Sharma 
5272832d4f2fSShashank Sharma 			/* Add 4:2:0(only) modes present in EDID */
5273537d9ed2SJani Nikula 			modes += do_y420vdb_modes(connector, vdb420,
5274537d9ed2SJani Nikula 						  cea_db_payload_len(db) - 1);
5275c858cfcaSDamien Lespiau 		}
527672794d16SJani Nikula 	}
527772794d16SJani Nikula 	cea_db_iter_end(&iter);
5278c858cfcaSDamien Lespiau 
527954ac76f8SChristian Schmidt 	return modes;
528054ac76f8SChristian Schmidt }
528154ac76f8SChristian Schmidt 
fixup_detailed_cea_mode_clock(struct drm_connector * connector,struct drm_display_mode * mode)5282e1e7bc48SJani Nikula static void fixup_detailed_cea_mode_clock(struct drm_connector *connector,
5283e1e7bc48SJani Nikula 					  struct drm_display_mode *mode)
5284fa3a7340SVille Syrjälä {
5285fa3a7340SVille Syrjälä 	const struct drm_display_mode *cea_mode;
5286fa3a7340SVille Syrjälä 	int clock1, clock2, clock;
5287d9278b4cSJani Nikula 	u8 vic;
5288fa3a7340SVille Syrjälä 	const char *type;
5289fa3a7340SVille Syrjälä 
52904c6bcf44SVille Syrjälä 	/*
52914c6bcf44SVille Syrjälä 	 * allow 5kHz clock difference either way to account for
52924c6bcf44SVille Syrjälä 	 * the 10kHz clock resolution limit of detailed timings.
52934c6bcf44SVille Syrjälä 	 */
5294d9278b4cSJani Nikula 	vic = drm_match_cea_mode_clock_tolerance(mode, 5);
5295d9278b4cSJani Nikula 	if (drm_valid_cea_vic(vic)) {
5296fa3a7340SVille Syrjälä 		type = "CEA";
52977befe621SVille Syrjälä 		cea_mode = cea_mode_for_vic(vic);
5298fa3a7340SVille Syrjälä 		clock1 = cea_mode->clock;
5299fa3a7340SVille Syrjälä 		clock2 = cea_mode_alternate_clock(cea_mode);
5300fa3a7340SVille Syrjälä 	} else {
5301d9278b4cSJani Nikula 		vic = drm_match_hdmi_mode_clock_tolerance(mode, 5);
5302d9278b4cSJani Nikula 		if (drm_valid_hdmi_vic(vic)) {
5303fa3a7340SVille Syrjälä 			type = "HDMI";
5304d9278b4cSJani Nikula 			cea_mode = &edid_4k_modes[vic];
5305fa3a7340SVille Syrjälä 			clock1 = cea_mode->clock;
5306fa3a7340SVille Syrjälä 			clock2 = hdmi_mode_alternate_clock(cea_mode);
5307fa3a7340SVille Syrjälä 		} else {
5308fa3a7340SVille Syrjälä 			return;
5309fa3a7340SVille Syrjälä 		}
5310fa3a7340SVille Syrjälä 	}
5311fa3a7340SVille Syrjälä 
5312fa3a7340SVille Syrjälä 	/* pick whichever is closest */
5313fa3a7340SVille Syrjälä 	if (abs(mode->clock - clock1) < abs(mode->clock - clock2))
5314fa3a7340SVille Syrjälä 		clock = clock1;
5315fa3a7340SVille Syrjälä 	else
5316fa3a7340SVille Syrjälä 		clock = clock2;
5317fa3a7340SVille Syrjälä 
5318fa3a7340SVille Syrjälä 	if (mode->clock == clock)
5319fa3a7340SVille Syrjälä 		return;
5320fa3a7340SVille Syrjälä 
5321e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev,
5322e1e7bc48SJani Nikula 		    "[CONNECTOR:%d:%s] detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
5323e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
5324d9278b4cSJani Nikula 		    type, vic, mode->clock, clock);
5325fa3a7340SVille Syrjälä 	mode->clock = clock;
5326fa3a7340SVille Syrjälä }
5327fa3a7340SVille Syrjälä 
drm_calculate_luminance_range(struct drm_connector * connector)532882068edeSJouni Högander static void drm_calculate_luminance_range(struct drm_connector *connector)
532982068edeSJouni Högander {
533082068edeSJouni Högander 	struct hdr_static_metadata *hdr_metadata = &connector->hdr_sink_metadata.hdmi_type1;
533182068edeSJouni Högander 	struct drm_luminance_range_info *luminance_range =
533282068edeSJouni Högander 		&connector->display_info.luminance_range;
533382068edeSJouni Högander 	static const u8 pre_computed_values[] = {
533482068edeSJouni Högander 		50, 51, 52, 53, 55, 56, 57, 58, 59, 61, 62, 63, 65, 66, 68, 69,
533582068edeSJouni Högander 		71, 72, 74, 75, 77, 79, 81, 82, 84, 86, 88, 90, 92, 94, 96, 98
533682068edeSJouni Högander 	};
533782068edeSJouni Högander 	u32 max_avg, min_cll, max, min, q, r;
533882068edeSJouni Högander 
533982068edeSJouni Högander 	if (!(hdr_metadata->metadata_type & BIT(HDMI_STATIC_METADATA_TYPE1)))
534082068edeSJouni Högander 		return;
534182068edeSJouni Högander 
534282068edeSJouni Högander 	max_avg = hdr_metadata->max_fall;
534382068edeSJouni Högander 	min_cll = hdr_metadata->min_cll;
534482068edeSJouni Högander 
534582068edeSJouni Högander 	/*
534682068edeSJouni Högander 	 * From the specification (CTA-861-G), for calculating the maximum
534782068edeSJouni Högander 	 * luminance we need to use:
534882068edeSJouni Högander 	 *	Luminance = 50*2**(CV/32)
534982068edeSJouni Högander 	 * Where CV is a one-byte value.
535082068edeSJouni Högander 	 * For calculating this expression we may need float point precision;
535182068edeSJouni Högander 	 * to avoid this complexity level, we take advantage that CV is divided
535282068edeSJouni Högander 	 * by a constant. From the Euclids division algorithm, we know that CV
535382068edeSJouni Högander 	 * can be written as: CV = 32*q + r. Next, we replace CV in the
535482068edeSJouni Högander 	 * Luminance expression and get 50*(2**q)*(2**(r/32)), hence we just
535582068edeSJouni Högander 	 * need to pre-compute the value of r/32. For pre-computing the values
535682068edeSJouni Högander 	 * We just used the following Ruby line:
535782068edeSJouni Högander 	 *	(0...32).each {|cv| puts (50*2**(cv/32.0)).round}
535882068edeSJouni Högander 	 * The results of the above expressions can be verified at
535982068edeSJouni Högander 	 * pre_computed_values.
536082068edeSJouni Högander 	 */
536182068edeSJouni Högander 	q = max_avg >> 5;
536282068edeSJouni Högander 	r = max_avg % 32;
536382068edeSJouni Högander 	max = (1 << q) * pre_computed_values[r];
536482068edeSJouni Högander 
536582068edeSJouni Högander 	/* min luminance: maxLum * (CV/255)^2 / 100 */
536682068edeSJouni Högander 	q = DIV_ROUND_CLOSEST(min_cll, 255);
536782068edeSJouni Högander 	min = max * DIV_ROUND_CLOSEST((q * q), 100);
536882068edeSJouni Högander 
536982068edeSJouni Högander 	luminance_range->min_luminance = min;
537082068edeSJouni Högander 	luminance_range->max_luminance = max;
537182068edeSJouni Högander }
537282068edeSJouni Högander 
eotf_supported(const u8 * edid_ext)5373e85959d6SUma Shankar static uint8_t eotf_supported(const u8 *edid_ext)
5374e85959d6SUma Shankar {
5375e85959d6SUma Shankar 	return edid_ext[2] &
5376e85959d6SUma Shankar 		(BIT(HDMI_EOTF_TRADITIONAL_GAMMA_SDR) |
5377e85959d6SUma Shankar 		 BIT(HDMI_EOTF_TRADITIONAL_GAMMA_HDR) |
5378b5e3eed1SVille Syrjälä 		 BIT(HDMI_EOTF_SMPTE_ST2084) |
5379b5e3eed1SVille Syrjälä 		 BIT(HDMI_EOTF_BT_2100_HLG));
5380e85959d6SUma Shankar }
5381e85959d6SUma Shankar 
hdr_metadata_type(const u8 * edid_ext)5382e85959d6SUma Shankar static uint8_t hdr_metadata_type(const u8 *edid_ext)
5383e85959d6SUma Shankar {
5384e85959d6SUma Shankar 	return edid_ext[3] &
5385e85959d6SUma Shankar 		BIT(HDMI_STATIC_METADATA_TYPE1);
5386e85959d6SUma Shankar }
5387e85959d6SUma Shankar 
5388e85959d6SUma Shankar static void
drm_parse_hdr_metadata_block(struct drm_connector * connector,const u8 * db)5389e85959d6SUma Shankar drm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db)
5390e85959d6SUma Shankar {
5391e85959d6SUma Shankar 	u16 len;
5392e85959d6SUma Shankar 
5393e85959d6SUma Shankar 	len = cea_db_payload_len(db);
5394e85959d6SUma Shankar 
5395e85959d6SUma Shankar 	connector->hdr_sink_metadata.hdmi_type1.eotf =
5396e85959d6SUma Shankar 						eotf_supported(db);
5397e85959d6SUma Shankar 	connector->hdr_sink_metadata.hdmi_type1.metadata_type =
5398e85959d6SUma Shankar 						hdr_metadata_type(db);
5399e85959d6SUma Shankar 
5400e85959d6SUma Shankar 	if (len >= 4)
5401e85959d6SUma Shankar 		connector->hdr_sink_metadata.hdmi_type1.max_cll = db[4];
5402e85959d6SUma Shankar 	if (len >= 5)
5403e85959d6SUma Shankar 		connector->hdr_sink_metadata.hdmi_type1.max_fall = db[5];
540482068edeSJouni Högander 	if (len >= 6) {
5405e85959d6SUma Shankar 		connector->hdr_sink_metadata.hdmi_type1.min_cll = db[6];
540682068edeSJouni Högander 
540782068edeSJouni Högander 		/* Calculate only when all values are available */
540882068edeSJouni Högander 		drm_calculate_luminance_range(connector);
540982068edeSJouni Högander 	}
5410e85959d6SUma Shankar }
5411e85959d6SUma Shankar 
54121ee3e217SJani Nikula /* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
541376adaa34SWu Fengguang static void
drm_parse_hdmi_vsdb_audio(struct drm_connector * connector,const u8 * db)541423ebf8b9SVille Syrjälä drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
541576adaa34SWu Fengguang {
54168504072aSVille Syrjälä 	u8 len = cea_db_payload_len(db);
54178504072aSVille Syrjälä 
5418f7da7785SJani Nikula 	if (len >= 6 && (db[6] & (1 << 7)))
5419f7da7785SJani Nikula 		connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI;
54201ee3e217SJani Nikula 
54211ee3e217SJani Nikula 	if (len >= 10 && hdmi_vsdb_latency_present(db)) {
54221ee3e217SJani Nikula 		connector->latency_present[0] = true;
542376adaa34SWu Fengguang 		connector->video_latency[0] = db[9];
542476adaa34SWu Fengguang 		connector->audio_latency[0] = db[10];
54251ee3e217SJani Nikula 	}
54261ee3e217SJani Nikula 
54271ee3e217SJani Nikula 	if (len >= 12 && hdmi_vsdb_i_latency_present(db)) {
54281ee3e217SJani Nikula 		connector->latency_present[1] = true;
542976adaa34SWu Fengguang 		connector->video_latency[1] = db[11];
543076adaa34SWu Fengguang 		connector->audio_latency[1] = db[12];
54311ee3e217SJani Nikula 	}
543276adaa34SWu Fengguang 
5433e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev,
5434e1e7bc48SJani Nikula 		    "[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n",
5435e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
5436e1e7bc48SJani Nikula 		    connector->latency_present[0], connector->latency_present[1],
5437e1e7bc48SJani Nikula 		    connector->video_latency[0], connector->video_latency[1],
5438e1e7bc48SJani Nikula 		    connector->audio_latency[0], connector->audio_latency[1]);
543976adaa34SWu Fengguang }
544076adaa34SWu Fengguang 
544176adaa34SWu Fengguang static void
monitor_name(const struct detailed_timing * timing,void * data)54424194442dSJani Nikula monitor_name(const struct detailed_timing *timing, void *data)
544376adaa34SWu Fengguang {
54444194442dSJani Nikula 	const char **res = data;
54454194442dSJani Nikula 
54464194442dSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME))
5447a7a131acSVille Syrjälä 		return;
5448a7a131acSVille Syrjälä 
54494194442dSJani Nikula 	*res = timing->data.other_data.data.str.str;
545076adaa34SWu Fengguang }
545176adaa34SWu Fengguang 
get_monitor_name(const struct drm_edid * drm_edid,char name[13])54522c54f87cSJani Nikula static int get_monitor_name(const struct drm_edid *drm_edid, char name[13])
545359f7c0faSJim Bride {
54544194442dSJani Nikula 	const char *edid_name = NULL;
545559f7c0faSJim Bride 	int mnl;
545659f7c0faSJim Bride 
54572c54f87cSJani Nikula 	if (!drm_edid || !name)
545859f7c0faSJim Bride 		return 0;
545959f7c0faSJim Bride 
546045aa2336SJani Nikula 	drm_for_each_detailed_block(drm_edid, monitor_name, &edid_name);
546159f7c0faSJim Bride 	for (mnl = 0; edid_name && mnl < 13; mnl++) {
546259f7c0faSJim Bride 		if (edid_name[mnl] == 0x0a)
546359f7c0faSJim Bride 			break;
546459f7c0faSJim Bride 
546559f7c0faSJim Bride 		name[mnl] = edid_name[mnl];
546659f7c0faSJim Bride 	}
546759f7c0faSJim Bride 
546859f7c0faSJim Bride 	return mnl;
546959f7c0faSJim Bride }
547059f7c0faSJim Bride 
547159f7c0faSJim Bride /**
547259f7c0faSJim Bride  * drm_edid_get_monitor_name - fetch the monitor name from the edid
547359f7c0faSJim Bride  * @edid: monitor EDID information
547459f7c0faSJim Bride  * @name: pointer to a character array to hold the name of the monitor
547559f7c0faSJim Bride  * @bufsize: The size of the name buffer (should be at least 14 chars.)
547659f7c0faSJim Bride  *
547759f7c0faSJim Bride  */
drm_edid_get_monitor_name(const struct edid * edid,char * name,int bufsize)5478f4e558ecSJani Nikula void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize)
547959f7c0faSJim Bride {
54802c54f87cSJani Nikula 	int name_length = 0;
548159f7c0faSJim Bride 
548259f7c0faSJim Bride 	if (bufsize <= 0)
548359f7c0faSJim Bride 		return;
548459f7c0faSJim Bride 
54852c54f87cSJani Nikula 	if (edid) {
54862c54f87cSJani Nikula 		char buf[13];
54872c54f87cSJani Nikula 		struct drm_edid drm_edid = {
54882c54f87cSJani Nikula 			.edid = edid,
54892c54f87cSJani Nikula 			.size = edid_size(edid),
54902c54f87cSJani Nikula 		};
54912c54f87cSJani Nikula 
54922c54f87cSJani Nikula 		name_length = min(get_monitor_name(&drm_edid, buf), bufsize - 1);
549359f7c0faSJim Bride 		memcpy(name, buf, name_length);
54942c54f87cSJani Nikula 	}
54952c54f87cSJani Nikula 
549659f7c0faSJim Bride 	name[name_length] = '\0';
549759f7c0faSJim Bride }
549859f7c0faSJim Bride EXPORT_SYMBOL(drm_edid_get_monitor_name);
549959f7c0faSJim Bride 
clear_eld(struct drm_connector * connector)550042750d39SJani Nikula static void clear_eld(struct drm_connector *connector)
550142750d39SJani Nikula {
5502*f2196ad7SDmitry Baryshkov 	mutex_lock(&connector->eld_mutex);
550342750d39SJani Nikula 	memset(connector->eld, 0, sizeof(connector->eld));
5504*f2196ad7SDmitry Baryshkov 	mutex_unlock(&connector->eld_mutex);
550542750d39SJani Nikula 
550642750d39SJani Nikula 	connector->latency_present[0] = false;
550742750d39SJani Nikula 	connector->latency_present[1] = false;
550842750d39SJani Nikula 	connector->video_latency[0] = 0;
550942750d39SJani Nikula 	connector->audio_latency[0] = 0;
551042750d39SJani Nikula 	connector->video_latency[1] = 0;
551142750d39SJani Nikula 	connector->audio_latency[1] = 0;
551242750d39SJani Nikula }
551342750d39SJani Nikula 
551479436a1cSJani Nikula /*
551576adaa34SWu Fengguang  * drm_edid_to_eld - build ELD from EDID
551676adaa34SWu Fengguang  * @connector: connector corresponding to the HDMI/DP sink
5517a2f9790dSJani Nikula  * @drm_edid: EDID to parse
551876adaa34SWu Fengguang  *
5519db6cf833SThierry Reding  * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
55201d1c3665SJani Nikula  * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
552176adaa34SWu Fengguang  */
drm_edid_to_eld(struct drm_connector * connector,const struct drm_edid * drm_edid)5522f4e558ecSJani Nikula static void drm_edid_to_eld(struct drm_connector *connector,
5523a2f9790dSJani Nikula 			    const struct drm_edid *drm_edid)
552476adaa34SWu Fengguang {
552558304630SJani Nikula 	const struct drm_display_info *info = &connector->display_info;
552637852141SJani Nikula 	const struct cea_db *db;
552737852141SJani Nikula 	struct cea_db_iter iter;
552876adaa34SWu Fengguang 	uint8_t *eld = connector->eld;
55297c018782SVille Syrjälä 	int total_sad_count = 0;
553076adaa34SWu Fengguang 	int mnl;
553176adaa34SWu Fengguang 
5532a2f9790dSJani Nikula 	if (!drm_edid)
5533e9bd0b84SJani Nikula 		return;
5534e9bd0b84SJani Nikula 
5535*f2196ad7SDmitry Baryshkov 	mutex_lock(&connector->eld_mutex);
5536*f2196ad7SDmitry Baryshkov 
55372c54f87cSJani Nikula 	mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
5538e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD monitor %s\n",
5539e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
5540e1e7bc48SJani Nikula 		    &eld[DRM_ELD_MONITOR_NAME_STRING]);
554159f7c0faSJim Bride 
554258304630SJani Nikula 	eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT;
5543f7da7785SJani Nikula 	eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl;
554476adaa34SWu Fengguang 
5545f7da7785SJani Nikula 	eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D;
554676adaa34SWu Fengguang 
5547a2f9790dSJani Nikula 	eld[DRM_ELD_MANUFACTURER_NAME0] = drm_edid->edid->mfg_id[0];
5548a2f9790dSJani Nikula 	eld[DRM_ELD_MANUFACTURER_NAME1] = drm_edid->edid->mfg_id[1];
5549a2f9790dSJani Nikula 	eld[DRM_ELD_PRODUCT_CODE0] = drm_edid->edid->prod_code[0];
5550a2f9790dSJani Nikula 	eld[DRM_ELD_PRODUCT_CODE1] = drm_edid->edid->prod_code[1];
555176adaa34SWu Fengguang 
55525e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
555337852141SJani Nikula 	cea_db_iter_for_each(db, &iter) {
555437852141SJani Nikula 		const u8 *data = cea_db_data(db);
555537852141SJani Nikula 		int len = cea_db_payload_len(db);
5556deec222eSKees Cook 		int sad_count;
555776adaa34SWu Fengguang 
55589e50b9d5SVille Syrjälä 		switch (cea_db_tag(db)) {
55599d72b7e2SJani Nikula 		case CTA_DB_AUDIO:
5560a0ab734dSChristian Schmidt 			/* Audio Data Block, contains SADs */
556137852141SJani Nikula 			sad_count = min(len / 3, 15 - total_sad_count);
55627c018782SVille Syrjälä 			if (sad_count >= 1)
5563f7da7785SJani Nikula 				memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
556437852141SJani Nikula 				       data, sad_count * 3);
55657c018782SVille Syrjälä 			total_sad_count += sad_count;
556676adaa34SWu Fengguang 			break;
55679d72b7e2SJani Nikula 		case CTA_DB_SPEAKER:
5568a0ab734dSChristian Schmidt 			/* Speaker Allocation Data Block */
556937852141SJani Nikula 			if (len >= 1)
557037852141SJani Nikula 				eld[DRM_ELD_SPEAKER] = data[0];
557176adaa34SWu Fengguang 			break;
55729d72b7e2SJani Nikula 		case CTA_DB_VENDOR:
557376adaa34SWu Fengguang 			/* HDMI Vendor-Specific Data Block */
557414f77fddSVille Syrjälä 			if (cea_db_is_hdmi_vsdb(db))
557537852141SJani Nikula 				drm_parse_hdmi_vsdb_audio(connector, (const u8 *)db);
557676adaa34SWu Fengguang 			break;
557776adaa34SWu Fengguang 		default:
557876adaa34SWu Fengguang 			break;
557976adaa34SWu Fengguang 		}
558076adaa34SWu Fengguang 	}
558137852141SJani Nikula 	cea_db_iter_end(&iter);
558237852141SJani Nikula 
5583f7da7785SJani Nikula 	eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT;
558476adaa34SWu Fengguang 
55851d1c3665SJani Nikula 	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
55861d1c3665SJani Nikula 	    connector->connector_type == DRM_MODE_CONNECTOR_eDP)
55871d1c3665SJani Nikula 		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
55881d1c3665SJani Nikula 	else
55891d1c3665SJani Nikula 		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
559076adaa34SWu Fengguang 
5591938fd8aaSJani Nikula 	eld[DRM_ELD_BASELINE_ELD_LEN] =
5592938fd8aaSJani Nikula 		DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
5593938fd8aaSJani Nikula 
5594e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD size %d, SAD count %d\n",
5595e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
55967c018782SVille Syrjälä 		    drm_eld_size(eld), total_sad_count);
5597*f2196ad7SDmitry Baryshkov 
5598*f2196ad7SDmitry Baryshkov 	mutex_unlock(&connector->eld_mutex);
559976adaa34SWu Fengguang }
560076adaa34SWu Fengguang 
_drm_edid_to_sad(const struct drm_edid * drm_edid,struct cea_sad ** sads)5601bba4b647SJani Nikula static int _drm_edid_to_sad(const struct drm_edid *drm_edid,
5602bba4b647SJani Nikula 			    struct cea_sad **sads)
5603fe214163SRafał Miłecki {
5604b07debc2SJani Nikula 	const struct cea_db *db;
5605b07debc2SJani Nikula 	struct cea_db_iter iter;
5606fe214163SRafał Miłecki 	int count = 0;
5607fe214163SRafał Miłecki 
56085e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
5609b07debc2SJani Nikula 	cea_db_iter_for_each(db, &iter) {
56109d72b7e2SJani Nikula 		if (cea_db_tag(db) == CTA_DB_AUDIO) {
5611fe214163SRafał Miłecki 			int j;
5612948de842SSuraj Upadhyay 
5613b07debc2SJani Nikula 			count = cea_db_payload_len(db) / 3; /* SAD is 3B */
5614fe214163SRafał Miłecki 			*sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
5615fe214163SRafał Miłecki 			if (!*sads)
5616fe214163SRafał Miłecki 				return -ENOMEM;
5617fe214163SRafał Miłecki 			for (j = 0; j < count; j++) {
5618b07debc2SJani Nikula 				const u8 *sad = &db->data[j * 3];
5619fe214163SRafał Miłecki 
5620fe214163SRafał Miłecki 				(*sads)[j].format = (sad[0] & 0x78) >> 3;
5621fe214163SRafał Miłecki 				(*sads)[j].channels = sad[0] & 0x7;
5622fe214163SRafał Miłecki 				(*sads)[j].freq = sad[1] & 0x7F;
5623fe214163SRafał Miłecki 				(*sads)[j].byte2 = sad[2];
5624fe214163SRafał Miłecki 			}
5625fe214163SRafał Miłecki 			break;
5626fe214163SRafał Miłecki 		}
5627fe214163SRafał Miłecki 	}
5628b07debc2SJani Nikula 	cea_db_iter_end(&iter);
5629b07debc2SJani Nikula 
5630b07debc2SJani Nikula 	DRM_DEBUG_KMS("Found %d Short Audio Descriptors\n", count);
5631fe214163SRafał Miłecki 
5632fe214163SRafał Miłecki 	return count;
5633fe214163SRafał Miłecki }
5634bba4b647SJani Nikula 
5635bba4b647SJani Nikula /**
5636bba4b647SJani Nikula  * drm_edid_to_sad - extracts SADs from EDID
5637bba4b647SJani Nikula  * @edid: EDID to parse
5638bba4b647SJani Nikula  * @sads: pointer that will be set to the extracted SADs
5639bba4b647SJani Nikula  *
5640bba4b647SJani Nikula  * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
5641bba4b647SJani Nikula  *
5642bba4b647SJani Nikula  * Note: The returned pointer needs to be freed using kfree().
5643bba4b647SJani Nikula  *
5644bba4b647SJani Nikula  * Return: The number of found SADs or negative number on error.
5645bba4b647SJani Nikula  */
drm_edid_to_sad(const struct edid * edid,struct cea_sad ** sads)5646bba4b647SJani Nikula int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads)
5647bba4b647SJani Nikula {
5648bba4b647SJani Nikula 	struct drm_edid drm_edid;
5649bba4b647SJani Nikula 
5650bba4b647SJani Nikula 	return _drm_edid_to_sad(drm_edid_legacy_init(&drm_edid, edid), sads);
5651bba4b647SJani Nikula }
5652fe214163SRafał Miłecki EXPORT_SYMBOL(drm_edid_to_sad);
5653fe214163SRafał Miłecki 
_drm_edid_to_speaker_allocation(const struct drm_edid * drm_edid,u8 ** sadb)565402703451SJani Nikula static int _drm_edid_to_speaker_allocation(const struct drm_edid *drm_edid,
565502703451SJani Nikula 					   u8 **sadb)
5656d105f476SAlex Deucher {
5657ed317307SJani Nikula 	const struct cea_db *db;
5658ed317307SJani Nikula 	struct cea_db_iter iter;
5659d105f476SAlex Deucher 	int count = 0;
5660d105f476SAlex Deucher 
56615e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
5662ed317307SJani Nikula 	cea_db_iter_for_each(db, &iter) {
5663ed317307SJani Nikula 		if (cea_db_tag(db) == CTA_DB_SPEAKER &&
5664ed317307SJani Nikula 		    cea_db_payload_len(db) == 3) {
5665ed317307SJani Nikula 			*sadb = kmemdup(db->data, cea_db_payload_len(db),
5666ed317307SJani Nikula 					GFP_KERNEL);
5667618e3776SAlex Deucher 			if (!*sadb)
5668618e3776SAlex Deucher 				return -ENOMEM;
5669ed317307SJani Nikula 			count = cea_db_payload_len(db);
5670d105f476SAlex Deucher 			break;
5671d105f476SAlex Deucher 		}
5672d105f476SAlex Deucher 	}
5673ed317307SJani Nikula 	cea_db_iter_end(&iter);
5674ed317307SJani Nikula 
5675ed317307SJani Nikula 	DRM_DEBUG_KMS("Found %d Speaker Allocation Data Blocks\n", count);
5676d105f476SAlex Deucher 
5677d105f476SAlex Deucher 	return count;
5678d105f476SAlex Deucher }
567902703451SJani Nikula 
568002703451SJani Nikula /**
568102703451SJani Nikula  * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
568202703451SJani Nikula  * @edid: EDID to parse
568302703451SJani Nikula  * @sadb: pointer to the speaker block
568402703451SJani Nikula  *
568502703451SJani Nikula  * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
568602703451SJani Nikula  *
568702703451SJani Nikula  * Note: The returned pointer needs to be freed using kfree().
568802703451SJani Nikula  *
568902703451SJani Nikula  * Return: The number of found Speaker Allocation Blocks or negative number on
569002703451SJani Nikula  * error.
569102703451SJani Nikula  */
drm_edid_to_speaker_allocation(const struct edid * edid,u8 ** sadb)569202703451SJani Nikula int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
569302703451SJani Nikula {
569402703451SJani Nikula 	struct drm_edid drm_edid;
569502703451SJani Nikula 
569602703451SJani Nikula 	return _drm_edid_to_speaker_allocation(drm_edid_legacy_init(&drm_edid, edid),
569702703451SJani Nikula 					       sadb);
569802703451SJani Nikula }
5699d105f476SAlex Deucher EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
5700d105f476SAlex Deucher 
5701d105f476SAlex Deucher /**
5702db6cf833SThierry Reding  * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
570376adaa34SWu Fengguang  * @connector: connector associated with the HDMI/DP sink
570476adaa34SWu Fengguang  * @mode: the display mode
5705db6cf833SThierry Reding  *
5706db6cf833SThierry Reding  * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
5707db6cf833SThierry Reding  * the sink doesn't support audio or video.
570876adaa34SWu Fengguang  */
drm_av_sync_delay(struct drm_connector * connector,const struct drm_display_mode * mode)570976adaa34SWu Fengguang int drm_av_sync_delay(struct drm_connector *connector,
57103a818d35SVille Syrjälä 		      const struct drm_display_mode *mode)
571176adaa34SWu Fengguang {
571276adaa34SWu Fengguang 	int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
571376adaa34SWu Fengguang 	int a, v;
571476adaa34SWu Fengguang 
571576adaa34SWu Fengguang 	if (!connector->latency_present[0])
571676adaa34SWu Fengguang 		return 0;
571776adaa34SWu Fengguang 	if (!connector->latency_present[1])
571876adaa34SWu Fengguang 		i = 0;
571976adaa34SWu Fengguang 
572076adaa34SWu Fengguang 	a = connector->audio_latency[i];
572176adaa34SWu Fengguang 	v = connector->video_latency[i];
572276adaa34SWu Fengguang 
572376adaa34SWu Fengguang 	/*
572476adaa34SWu Fengguang 	 * HDMI/DP sink doesn't support audio or video?
572576adaa34SWu Fengguang 	 */
572676adaa34SWu Fengguang 	if (a == 255 || v == 255)
572776adaa34SWu Fengguang 		return 0;
572876adaa34SWu Fengguang 
572976adaa34SWu Fengguang 	/*
573076adaa34SWu Fengguang 	 * Convert raw EDID values to millisecond.
573176adaa34SWu Fengguang 	 * Treat unknown latency as 0ms.
573276adaa34SWu Fengguang 	 */
573376adaa34SWu Fengguang 	if (a)
573476adaa34SWu Fengguang 		a = min(2 * (a - 1), 500);
573576adaa34SWu Fengguang 	if (v)
573676adaa34SWu Fengguang 		v = min(2 * (v - 1), 500);
573776adaa34SWu Fengguang 
573876adaa34SWu Fengguang 	return max(v - a, 0);
573976adaa34SWu Fengguang }
574076adaa34SWu Fengguang EXPORT_SYMBOL(drm_av_sync_delay);
574176adaa34SWu Fengguang 
_drm_detect_hdmi_monitor(const struct drm_edid * drm_edid)57423176d092SJani Nikula static bool _drm_detect_hdmi_monitor(const struct drm_edid *drm_edid)
57433176d092SJani Nikula {
57443176d092SJani Nikula 	const struct cea_db *db;
57453176d092SJani Nikula 	struct cea_db_iter iter;
57463176d092SJani Nikula 	bool hdmi = false;
57473176d092SJani Nikula 
57483176d092SJani Nikula 	/*
57493176d092SJani Nikula 	 * Because HDMI identifier is in Vendor Specific Block,
57503176d092SJani Nikula 	 * search it from all data blocks of CEA extension.
57513176d092SJani Nikula 	 */
57525e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
57533176d092SJani Nikula 	cea_db_iter_for_each(db, &iter) {
57543176d092SJani Nikula 		if (cea_db_is_hdmi_vsdb(db)) {
57553176d092SJani Nikula 			hdmi = true;
57563176d092SJani Nikula 			break;
57573176d092SJani Nikula 		}
57583176d092SJani Nikula 	}
57593176d092SJani Nikula 	cea_db_iter_end(&iter);
57603176d092SJani Nikula 
57613176d092SJani Nikula 	return hdmi;
57623176d092SJani Nikula }
57633176d092SJani Nikula 
576476adaa34SWu Fengguang /**
5765db6cf833SThierry Reding  * drm_detect_hdmi_monitor - detect whether monitor is HDMI
5766f23c20c8SMa Ling  * @edid: monitor EDID information
5767f23c20c8SMa Ling  *
5768f23c20c8SMa Ling  * Parse the CEA extension according to CEA-861-B.
5769db6cf833SThierry Reding  *
5770a92d083dSLaurent Pinchart  * Drivers that have added the modes parsed from EDID to drm_display_info
5771a92d083dSLaurent Pinchart  * should use &drm_display_info.is_hdmi instead of calling this function.
5772a92d083dSLaurent Pinchart  *
5773db6cf833SThierry Reding  * Return: True if the monitor is HDMI, false if not or unknown.
5774f23c20c8SMa Ling  */
drm_detect_hdmi_monitor(const struct edid * edid)5775f4e558ecSJani Nikula bool drm_detect_hdmi_monitor(const struct edid *edid)
5776f23c20c8SMa Ling {
57773176d092SJani Nikula 	struct drm_edid drm_edid;
5778f23c20c8SMa Ling 
57793176d092SJani Nikula 	return _drm_detect_hdmi_monitor(drm_edid_legacy_init(&drm_edid, edid));
5780f23c20c8SMa Ling }
5781f23c20c8SMa Ling EXPORT_SYMBOL(drm_detect_hdmi_monitor);
5782f23c20c8SMa Ling 
_drm_detect_monitor_audio(const struct drm_edid * drm_edid)57830c057877SJani Nikula static bool _drm_detect_monitor_audio(const struct drm_edid *drm_edid)
57848fe9790dSZhenyu Wang {
5785705bec3eSJani Nikula 	struct drm_edid_iter edid_iter;
57869975af04SJani Nikula 	const struct cea_db *db;
57879975af04SJani Nikula 	struct cea_db_iter iter;
578843d16d84SJani Nikula 	const u8 *edid_ext;
57898fe9790dSZhenyu Wang 	bool has_audio = false;
57908fe9790dSZhenyu Wang 
5791bbded689SJani Nikula 	drm_edid_iter_begin(drm_edid, &edid_iter);
5792705bec3eSJani Nikula 	drm_edid_iter_for_each(edid_ext, &edid_iter) {
5793705bec3eSJani Nikula 		if (edid_ext[0] == CEA_EXT) {
5794705bec3eSJani Nikula 			has_audio = edid_ext[3] & EDID_BASIC_AUDIO;
5795705bec3eSJani Nikula 			if (has_audio)
5796705bec3eSJani Nikula 				break;
5797705bec3eSJani Nikula 		}
5798705bec3eSJani Nikula 	}
5799705bec3eSJani Nikula 	drm_edid_iter_end(&edid_iter);
58008fe9790dSZhenyu Wang 
58018fe9790dSZhenyu Wang 	if (has_audio) {
58028fe9790dSZhenyu Wang 		DRM_DEBUG_KMS("Monitor has basic audio support\n");
58038fe9790dSZhenyu Wang 		goto end;
58048fe9790dSZhenyu Wang 	}
58058fe9790dSZhenyu Wang 
58065e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
58079975af04SJani Nikula 	cea_db_iter_for_each(db, &iter) {
58089975af04SJani Nikula 		if (cea_db_tag(db) == CTA_DB_AUDIO) {
58099975af04SJani Nikula 			const u8 *data = cea_db_data(db);
58109975af04SJani Nikula 			int i;
58118fe9790dSZhenyu Wang 
58129975af04SJani Nikula 			for (i = 0; i < cea_db_payload_len(db); i += 3)
58138fe9790dSZhenyu Wang 				DRM_DEBUG_KMS("CEA audio format %d\n",
58149975af04SJani Nikula 					      (data[i] >> 3) & 0xf);
58159975af04SJani Nikula 			has_audio = true;
58169975af04SJani Nikula 			break;
58178fe9790dSZhenyu Wang 		}
58188fe9790dSZhenyu Wang 	}
58199975af04SJani Nikula 	cea_db_iter_end(&iter);
58209975af04SJani Nikula 
58218fe9790dSZhenyu Wang end:
58228fe9790dSZhenyu Wang 	return has_audio;
58238fe9790dSZhenyu Wang }
58240c057877SJani Nikula 
58250c057877SJani Nikula /**
58260c057877SJani Nikula  * drm_detect_monitor_audio - check monitor audio capability
58270c057877SJani Nikula  * @edid: EDID block to scan
58280c057877SJani Nikula  *
58290c057877SJani Nikula  * Monitor should have CEA extension block.
58300c057877SJani Nikula  * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
58310c057877SJani Nikula  * audio' only. If there is any audio extension block and supported
58320c057877SJani Nikula  * audio format, assume at least 'basic audio' support, even if 'basic
58330c057877SJani Nikula  * audio' is not defined in EDID.
58340c057877SJani Nikula  *
58350c057877SJani Nikula  * Return: True if the monitor supports audio, false otherwise.
58360c057877SJani Nikula  */
drm_detect_monitor_audio(const struct edid * edid)58370c057877SJani Nikula bool drm_detect_monitor_audio(const struct edid *edid)
58380c057877SJani Nikula {
58390c057877SJani Nikula 	struct drm_edid drm_edid;
58400c057877SJani Nikula 
58410c057877SJani Nikula 	return _drm_detect_monitor_audio(drm_edid_legacy_init(&drm_edid, edid));
58420c057877SJani Nikula }
58438fe9790dSZhenyu Wang EXPORT_SYMBOL(drm_detect_monitor_audio);
58448fe9790dSZhenyu Wang 
5845b1edd6a6SVille Syrjälä 
5846c8127cf0SVille Syrjälä /**
5847c8127cf0SVille Syrjälä  * drm_default_rgb_quant_range - default RGB quantization range
5848c8127cf0SVille Syrjälä  * @mode: display mode
5849c8127cf0SVille Syrjälä  *
5850c8127cf0SVille Syrjälä  * Determine the default RGB quantization range for the mode,
5851c8127cf0SVille Syrjälä  * as specified in CEA-861.
5852c8127cf0SVille Syrjälä  *
5853c8127cf0SVille Syrjälä  * Return: The default RGB quantization range for the mode
5854c8127cf0SVille Syrjälä  */
5855c8127cf0SVille Syrjälä enum hdmi_quantization_range
drm_default_rgb_quant_range(const struct drm_display_mode * mode)5856c8127cf0SVille Syrjälä drm_default_rgb_quant_range(const struct drm_display_mode *mode)
5857c8127cf0SVille Syrjälä {
5858c8127cf0SVille Syrjälä 	/* All CEA modes other than VIC 1 use limited quantization range. */
5859c8127cf0SVille Syrjälä 	return drm_match_cea_mode(mode) > 1 ?
5860c8127cf0SVille Syrjälä 		HDMI_QUANTIZATION_RANGE_LIMITED :
5861c8127cf0SVille Syrjälä 		HDMI_QUANTIZATION_RANGE_FULL;
5862c8127cf0SVille Syrjälä }
5863c8127cf0SVille Syrjälä EXPORT_SYMBOL(drm_default_rgb_quant_range);
5864c8127cf0SVille Syrjälä 
5865c3292ab5SJani Nikula /* CTA-861 Video Data Block (CTA VDB) */
parse_cta_vdb(struct drm_connector * connector,const struct cea_db * db)5866c3292ab5SJani Nikula static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db)
5867c3292ab5SJani Nikula {
5868c3292ab5SJani Nikula 	struct drm_display_info *info = &connector->display_info;
5869c3292ab5SJani Nikula 	int i, vic_index, len = cea_db_payload_len(db);
5870c3292ab5SJani Nikula 	const u8 *svds = cea_db_data(db);
5871c3292ab5SJani Nikula 	u8 *vics;
5872c3292ab5SJani Nikula 
5873c3292ab5SJani Nikula 	if (!len)
5874c3292ab5SJani Nikula 		return;
5875c3292ab5SJani Nikula 
5876c3292ab5SJani Nikula 	/* Gracefully handle multiple VDBs, however unlikely that is */
5877c3292ab5SJani Nikula 	vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL);
5878c3292ab5SJani Nikula 	if (!vics)
5879c3292ab5SJani Nikula 		return;
5880c3292ab5SJani Nikula 
5881c3292ab5SJani Nikula 	vic_index = info->vics_len;
5882c3292ab5SJani Nikula 	info->vics_len += len;
5883c3292ab5SJani Nikula 	info->vics = vics;
5884c3292ab5SJani Nikula 
5885c3292ab5SJani Nikula 	for (i = 0; i < len; i++) {
5886c3292ab5SJani Nikula 		u8 vic = svd_to_vic(svds[i]);
5887c3292ab5SJani Nikula 
5888c3292ab5SJani Nikula 		if (!drm_valid_cea_vic(vic))
5889c3292ab5SJani Nikula 			vic = 0;
5890c3292ab5SJani Nikula 
5891c3292ab5SJani Nikula 		info->vics[vic_index++] = vic;
5892c3292ab5SJani Nikula 	}
5893c3292ab5SJani Nikula }
5894c3292ab5SJani Nikula 
589561e05fdcSJani Nikula /*
589661e05fdcSJani Nikula  * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB.
589761e05fdcSJani Nikula  *
589861e05fdcSJani Nikula  * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed
589961e05fdcSJani Nikula  * using the VICs themselves.
590061e05fdcSJani Nikula  */
update_cta_y420cmdb(struct drm_connector * connector,u64 y420cmdb_map)590161e05fdcSJani Nikula static void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map)
590261e05fdcSJani Nikula {
590361e05fdcSJani Nikula 	struct drm_display_info *info = &connector->display_info;
590461e05fdcSJani Nikula 	struct drm_hdmi_info *hdmi = &info->hdmi;
590561e05fdcSJani Nikula 	int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map));
590661e05fdcSJani Nikula 
590761e05fdcSJani Nikula 	for (i = 0; i < len; i++) {
590861e05fdcSJani Nikula 		u8 vic = info->vics[i];
590961e05fdcSJani Nikula 
591061e05fdcSJani Nikula 		if (vic && y420cmdb_map & BIT_ULL(i))
591161e05fdcSJani Nikula 			bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
591261e05fdcSJani Nikula 	}
591361e05fdcSJani Nikula }
591461e05fdcSJani Nikula 
cta_vdb_has_vic(const struct drm_connector * connector,u8 vic)59154ed29f39SJani Nikula static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic)
59164ed29f39SJani Nikula {
59174ed29f39SJani Nikula 	const struct drm_display_info *info = &connector->display_info;
59184ed29f39SJani Nikula 	int i;
59194ed29f39SJani Nikula 
59204ed29f39SJani Nikula 	if (!vic || !info->vics)
59214ed29f39SJani Nikula 		return false;
59224ed29f39SJani Nikula 
59234ed29f39SJani Nikula 	for (i = 0; i < info->vics_len; i++) {
59244ed29f39SJani Nikula 		if (info->vics[i] == vic)
59254ed29f39SJani Nikula 			return true;
59264ed29f39SJani Nikula 	}
59274ed29f39SJani Nikula 
59284ed29f39SJani Nikula 	return false;
59294ed29f39SJani Nikula }
59304ed29f39SJani Nikula 
5931c54e2e23SJani Nikula /* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */
parse_cta_y420vdb(struct drm_connector * connector,const struct cea_db * db)5932c54e2e23SJani Nikula static void parse_cta_y420vdb(struct drm_connector *connector,
5933c54e2e23SJani Nikula 			      const struct cea_db *db)
5934c54e2e23SJani Nikula {
5935c54e2e23SJani Nikula 	struct drm_display_info *info = &connector->display_info;
5936c54e2e23SJani Nikula 	struct drm_hdmi_info *hdmi = &info->hdmi;
5937c54e2e23SJani Nikula 	const u8 *svds = cea_db_data(db) + 1;
5938c54e2e23SJani Nikula 	int i;
5939c54e2e23SJani Nikula 
5940c54e2e23SJani Nikula 	for (i = 0; i < cea_db_payload_len(db) - 1; i++) {
5941c54e2e23SJani Nikula 		u8 vic = svd_to_vic(svds[i]);
5942c54e2e23SJani Nikula 
5943c54e2e23SJani Nikula 		if (!drm_valid_cea_vic(vic))
5944c54e2e23SJani Nikula 			continue;
5945c54e2e23SJani Nikula 
5946c54e2e23SJani Nikula 		bitmap_set(hdmi->y420_vdb_modes, vic, 1);
5947c54e2e23SJani Nikula 		info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
5948c54e2e23SJani Nikula 	}
5949c54e2e23SJani Nikula }
5950c54e2e23SJani Nikula 
drm_parse_vcdb(struct drm_connector * connector,const u8 * db)59511581b2dfSVille Syrjälä static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db)
59521581b2dfSVille Syrjälä {
59531581b2dfSVille Syrjälä 	struct drm_display_info *info = &connector->display_info;
59541581b2dfSVille Syrjälä 
5955e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] CEA VCDB 0x%02x\n",
5956e1e7bc48SJani Nikula 		    connector->base.id, connector->name, db[2]);
59571581b2dfSVille Syrjälä 
59581581b2dfSVille Syrjälä 	if (db[2] & EDID_CEA_VCDB_QS)
59591581b2dfSVille Syrjälä 		info->rgb_quant_range_selectable = true;
59601581b2dfSVille Syrjälä }
59611581b2dfSVille Syrjälä 
59624499d488SSwati Sharma static
drm_get_max_frl_rate(int max_frl_rate,u8 * max_lanes,u8 * max_rate_per_lane)59634499d488SSwati Sharma void drm_get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane)
59644499d488SSwati Sharma {
59654499d488SSwati Sharma 	switch (max_frl_rate) {
59664499d488SSwati Sharma 	case 1:
59674499d488SSwati Sharma 		*max_lanes = 3;
59684499d488SSwati Sharma 		*max_rate_per_lane = 3;
59694499d488SSwati Sharma 		break;
59704499d488SSwati Sharma 	case 2:
59714499d488SSwati Sharma 		*max_lanes = 3;
59724499d488SSwati Sharma 		*max_rate_per_lane = 6;
59734499d488SSwati Sharma 		break;
59744499d488SSwati Sharma 	case 3:
59754499d488SSwati Sharma 		*max_lanes = 4;
59764499d488SSwati Sharma 		*max_rate_per_lane = 6;
59774499d488SSwati Sharma 		break;
59784499d488SSwati Sharma 	case 4:
59794499d488SSwati Sharma 		*max_lanes = 4;
59804499d488SSwati Sharma 		*max_rate_per_lane = 8;
59814499d488SSwati Sharma 		break;
59824499d488SSwati Sharma 	case 5:
59834499d488SSwati Sharma 		*max_lanes = 4;
59844499d488SSwati Sharma 		*max_rate_per_lane = 10;
59854499d488SSwati Sharma 		break;
59864499d488SSwati Sharma 	case 6:
59874499d488SSwati Sharma 		*max_lanes = 4;
59884499d488SSwati Sharma 		*max_rate_per_lane = 12;
59894499d488SSwati Sharma 		break;
59904499d488SSwati Sharma 	case 0:
59914499d488SSwati Sharma 	default:
59924499d488SSwati Sharma 		*max_lanes = 0;
59934499d488SSwati Sharma 		*max_rate_per_lane = 0;
59944499d488SSwati Sharma 	}
59954499d488SSwati Sharma }
59964499d488SSwati Sharma 
drm_parse_ycbcr420_deep_color_info(struct drm_connector * connector,const u8 * db)5997e6a9a2c3SShashank Sharma static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
5998e6a9a2c3SShashank Sharma 					       const u8 *db)
5999e6a9a2c3SShashank Sharma {
6000e6a9a2c3SShashank Sharma 	u8 dc_mask;
6001e6a9a2c3SShashank Sharma 	struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
6002e6a9a2c3SShashank Sharma 
6003e6a9a2c3SShashank Sharma 	dc_mask = db[7] & DRM_EDID_YCBCR420_DC_MASK;
60049068e02fSClint Taylor 	hdmi->y420_dc_modes = dc_mask;
6005e6a9a2c3SShashank Sharma }
6006e6a9a2c3SShashank Sharma 
drm_parse_dsc_info(struct drm_hdmi_dsc_cap * hdmi_dsc,const u8 * hf_scds)60075e706c4dSAnkit Nautiyal static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc,
60085e706c4dSAnkit Nautiyal 			       const u8 *hf_scds)
60095e706c4dSAnkit Nautiyal {
60105e706c4dSAnkit Nautiyal 	hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2;
60115e706c4dSAnkit Nautiyal 
60125e706c4dSAnkit Nautiyal 	if (!hdmi_dsc->v_1p2)
60135e706c4dSAnkit Nautiyal 		return;
60145e706c4dSAnkit Nautiyal 
60155e706c4dSAnkit Nautiyal 	hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420;
60165e706c4dSAnkit Nautiyal 	hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP;
60175e706c4dSAnkit Nautiyal 
60185e706c4dSAnkit Nautiyal 	if (hf_scds[11] & DRM_EDID_DSC_16BPC)
60195e706c4dSAnkit Nautiyal 		hdmi_dsc->bpc_supported = 16;
60205e706c4dSAnkit Nautiyal 	else if (hf_scds[11] & DRM_EDID_DSC_12BPC)
60215e706c4dSAnkit Nautiyal 		hdmi_dsc->bpc_supported = 12;
60225e706c4dSAnkit Nautiyal 	else if (hf_scds[11] & DRM_EDID_DSC_10BPC)
60235e706c4dSAnkit Nautiyal 		hdmi_dsc->bpc_supported = 10;
60245e706c4dSAnkit Nautiyal 	else
60255e706c4dSAnkit Nautiyal 		/* Supports min 8 BPC if DSC 1.2 is supported*/
60265e706c4dSAnkit Nautiyal 		hdmi_dsc->bpc_supported = 8;
60275e706c4dSAnkit Nautiyal 
6028a07e6f56SAnkit Nautiyal 	if (cea_db_payload_len(hf_scds) >= 12 && hf_scds[12]) {
6029a07e6f56SAnkit Nautiyal 		u8 dsc_max_slices;
6030a07e6f56SAnkit Nautiyal 		u8 dsc_max_frl_rate;
6031a07e6f56SAnkit Nautiyal 
60325e706c4dSAnkit Nautiyal 		dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
60335e706c4dSAnkit Nautiyal 		drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes,
60345e706c4dSAnkit Nautiyal 				     &hdmi_dsc->max_frl_rate_per_lane);
60355e706c4dSAnkit Nautiyal 
60365e706c4dSAnkit Nautiyal 		dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES;
60375e706c4dSAnkit Nautiyal 
60385e706c4dSAnkit Nautiyal 		switch (dsc_max_slices) {
60395e706c4dSAnkit Nautiyal 		case 1:
60405e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 1;
60415e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 340;
60425e706c4dSAnkit Nautiyal 			break;
60435e706c4dSAnkit Nautiyal 		case 2:
60445e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 2;
60455e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 340;
60465e706c4dSAnkit Nautiyal 			break;
60475e706c4dSAnkit Nautiyal 		case 3:
60485e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 4;
60495e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 340;
60505e706c4dSAnkit Nautiyal 			break;
60515e706c4dSAnkit Nautiyal 		case 4:
60525e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 8;
60535e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 340;
60545e706c4dSAnkit Nautiyal 			break;
60555e706c4dSAnkit Nautiyal 		case 5:
60565e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 8;
60575e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 400;
60585e706c4dSAnkit Nautiyal 			break;
60595e706c4dSAnkit Nautiyal 		case 6:
60605e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 12;
60615e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 400;
60625e706c4dSAnkit Nautiyal 			break;
60635e706c4dSAnkit Nautiyal 		case 7:
60645e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 16;
60655e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 400;
60665e706c4dSAnkit Nautiyal 			break;
60675e706c4dSAnkit Nautiyal 		case 0:
60685e706c4dSAnkit Nautiyal 		default:
60695e706c4dSAnkit Nautiyal 			hdmi_dsc->max_slices = 0;
60705e706c4dSAnkit Nautiyal 			hdmi_dsc->clk_per_slice = 0;
60715e706c4dSAnkit Nautiyal 		}
60725e706c4dSAnkit Nautiyal 	}
60735e706c4dSAnkit Nautiyal 
6074a07e6f56SAnkit Nautiyal 	if (cea_db_payload_len(hf_scds) >= 13 && hf_scds[13])
6075a07e6f56SAnkit Nautiyal 		hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
6076a07e6f56SAnkit Nautiyal }
6077a07e6f56SAnkit Nautiyal 
6078d8cb49d2SJani Nikula /* Sink Capability Data Structure */
drm_parse_hdmi_forum_scds(struct drm_connector * connector,const u8 * hf_scds)6079d8cb49d2SJani Nikula static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
6080d8cb49d2SJani Nikula 				      const u8 *hf_scds)
6081afa1c763SShashank Sharma {
608226c2ff77SJani Nikula 	struct drm_display_info *info = &connector->display_info;
608326c2ff77SJani Nikula 	struct drm_hdmi_info *hdmi = &info->hdmi;
6084a07e6f56SAnkit Nautiyal 	struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
60855e931c88SAnkit Nautiyal 	int max_tmds_clock = 0;
60865e931c88SAnkit Nautiyal 	u8 max_frl_rate = 0;
60875e931c88SAnkit Nautiyal 	bool dsc_support = false;
6088afa1c763SShashank Sharma 
608926c2ff77SJani Nikula 	info->has_hdmi_infoframe = true;
6090f1781e9bSVille Syrjälä 
6091d8cb49d2SJani Nikula 	if (hf_scds[6] & 0x80) {
6092afa1c763SShashank Sharma 		hdmi->scdc.supported = true;
6093d8cb49d2SJani Nikula 		if (hf_scds[6] & 0x40)
6094afa1c763SShashank Sharma 			hdmi->scdc.read_request = true;
6095afa1c763SShashank Sharma 	}
609662c58af3SShashank Sharma 
609762c58af3SShashank Sharma 	/*
609862c58af3SShashank Sharma 	 * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
609962c58af3SShashank Sharma 	 * And as per the spec, three factors confirm this:
610062c58af3SShashank Sharma 	 * * Availability of a HF-VSDB block in EDID (check)
610162c58af3SShashank Sharma 	 * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
610262c58af3SShashank Sharma 	 * * SCDC support available (let's check)
610362c58af3SShashank Sharma 	 * Lets check it out.
610462c58af3SShashank Sharma 	 */
610562c58af3SShashank Sharma 
6106d8cb49d2SJani Nikula 	if (hf_scds[5]) {
610762c58af3SShashank Sharma 		struct drm_scdc *scdc = &hdmi->scdc;
610862c58af3SShashank Sharma 
61095e931c88SAnkit Nautiyal 		/* max clock is 5000 KHz times block value */
61105e931c88SAnkit Nautiyal 		max_tmds_clock = hf_scds[5] * 5000;
61115e931c88SAnkit Nautiyal 
611262c58af3SShashank Sharma 		if (max_tmds_clock > 340000) {
611326c2ff77SJani Nikula 			info->max_tmds_clock = max_tmds_clock;
611462c58af3SShashank Sharma 		}
611562c58af3SShashank Sharma 
611662c58af3SShashank Sharma 		if (scdc->supported) {
611762c58af3SShashank Sharma 			scdc->scrambling.supported = true;
611862c58af3SShashank Sharma 
6119dbe2d2bfSThierry Reding 			/* Few sinks support scrambling for clocks < 340M */
6120d8cb49d2SJani Nikula 			if ((hf_scds[6] & 0x8))
612162c58af3SShashank Sharma 				scdc->scrambling.low_rates = true;
612262c58af3SShashank Sharma 		}
612362c58af3SShashank Sharma 	}
6124e6a9a2c3SShashank Sharma 
6125d8cb49d2SJani Nikula 	if (hf_scds[7]) {
6126d8cb49d2SJani Nikula 		max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
61274499d488SSwati Sharma 		drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes,
61284499d488SSwati Sharma 				     &hdmi->max_frl_rate_per_lane);
61294499d488SSwati Sharma 	}
61304499d488SSwati Sharma 
6131d8cb49d2SJani Nikula 	drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
6132a07e6f56SAnkit Nautiyal 
61335e931c88SAnkit Nautiyal 	if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) {
6134a07e6f56SAnkit Nautiyal 		drm_parse_dsc_info(hdmi_dsc, hf_scds);
61355e931c88SAnkit Nautiyal 		dsc_support = true;
61365e931c88SAnkit Nautiyal 	}
61375e931c88SAnkit Nautiyal 
61385e931c88SAnkit Nautiyal 	drm_dbg_kms(connector->dev,
613966d17ecdSJani Nikula 		    "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n",
614066d17ecdSJani Nikula 		    connector->base.id, connector->name,
61415e931c88SAnkit Nautiyal 		    max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support));
6142afa1c763SShashank Sharma }
6143afa1c763SShashank Sharma 
drm_parse_hdmi_deep_color_info(struct drm_connector * connector,const u8 * hdmi)61441cea146aSVille Syrjälä static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
61451cea146aSVille Syrjälä 					   const u8 *hdmi)
6146d0c94692SMario Kleiner {
61471826750fSVille Syrjälä 	struct drm_display_info *info = &connector->display_info;
6148d0c94692SMario Kleiner 	unsigned int dc_bpc = 0;
6149d0c94692SMario Kleiner 
6150d0c94692SMario Kleiner 	/* HDMI supports at least 8 bpc */
6151d0c94692SMario Kleiner 	info->bpc = 8;
6152d0c94692SMario Kleiner 
6153d0c94692SMario Kleiner 	if (cea_db_payload_len(hdmi) < 6)
61541cea146aSVille Syrjälä 		return;
6155d0c94692SMario Kleiner 
6156d0c94692SMario Kleiner 	if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
6157d0c94692SMario Kleiner 		dc_bpc = 10;
61584adc33f3SMaxime Ripard 		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_30;
6159e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 30.\n",
6160e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
6161d0c94692SMario Kleiner 	}
6162d0c94692SMario Kleiner 
6163d0c94692SMario Kleiner 	if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
6164d0c94692SMario Kleiner 		dc_bpc = 12;
61654adc33f3SMaxime Ripard 		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_36;
6166e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 36.\n",
6167e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
6168d0c94692SMario Kleiner 	}
6169d0c94692SMario Kleiner 
6170d0c94692SMario Kleiner 	if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
6171d0c94692SMario Kleiner 		dc_bpc = 16;
61724adc33f3SMaxime Ripard 		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_48;
6173e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 48.\n",
6174e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
6175d0c94692SMario Kleiner 	}
6176d0c94692SMario Kleiner 
61771cea146aSVille Syrjälä 	if (dc_bpc == 0) {
6178e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] No deep color support on this HDMI sink.\n",
6179e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
61801cea146aSVille Syrjälä 		return;
61811cea146aSVille Syrjälä 	}
61821cea146aSVille Syrjälä 
6183e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Assigning HDMI sink color depth as %d bpc.\n",
6184e1e7bc48SJani Nikula 		    connector->base.id, connector->name, dc_bpc);
6185d0c94692SMario Kleiner 	info->bpc = dc_bpc;
6186d0c94692SMario Kleiner 
6187d0c94692SMario Kleiner 	/* YCRCB444 is optional according to spec. */
6188d0c94692SMario Kleiner 	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
61894adc33f3SMaxime Ripard 		info->edid_hdmi_ycbcr444_dc_modes = info->edid_hdmi_rgb444_dc_modes;
6190e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does YCRCB444 in deep color.\n",
6191e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
6192d0c94692SMario Kleiner 	}
6193d0c94692SMario Kleiner 
6194d0c94692SMario Kleiner 	/*
6195d0c94692SMario Kleiner 	 * Spec says that if any deep color mode is supported at all,
6196d0c94692SMario Kleiner 	 * then deep color 36 bit must be supported.
6197d0c94692SMario Kleiner 	 */
6198d0c94692SMario Kleiner 	if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
6199e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink should do DC_36, but does not!\n",
6200e1e7bc48SJani Nikula 			    connector->base.id, connector->name);
6201d0c94692SMario Kleiner 	}
6202d0c94692SMario Kleiner }
6203d0c94692SMario Kleiner 
6204919d320fSJani Nikula /* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
620523ebf8b9SVille Syrjälä static void
drm_parse_hdmi_vsdb_video(struct drm_connector * connector,const u8 * db)620623ebf8b9SVille Syrjälä drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
620723ebf8b9SVille Syrjälä {
620823ebf8b9SVille Syrjälä 	struct drm_display_info *info = &connector->display_info;
620923ebf8b9SVille Syrjälä 	u8 len = cea_db_payload_len(db);
621023ebf8b9SVille Syrjälä 
6211a92d083dSLaurent Pinchart 	info->is_hdmi = true;
6212a92d083dSLaurent Pinchart 
621323ebf8b9SVille Syrjälä 	if (len >= 6)
621423ebf8b9SVille Syrjälä 		info->dvi_dual = db[6] & 1;
621523ebf8b9SVille Syrjälä 	if (len >= 7)
621623ebf8b9SVille Syrjälä 		info->max_tmds_clock = db[7] * 5000;
621723ebf8b9SVille Syrjälä 
6218919d320fSJani Nikula 	/*
6219919d320fSJani Nikula 	 * Try to infer whether the sink supports HDMI infoframes.
6220919d320fSJani Nikula 	 *
6221919d320fSJani Nikula 	 * HDMI infoframe support was first added in HDMI 1.4. Assume the sink
6222919d320fSJani Nikula 	 * supports infoframes if HDMI_Video_present is set.
6223919d320fSJani Nikula 	 */
6224919d320fSJani Nikula 	if (len >= 8 && db[8] & BIT(5))
6225919d320fSJani Nikula 		info->has_hdmi_infoframe = true;
6226919d320fSJani Nikula 
6227e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n",
6228e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
6229e1e7bc48SJani Nikula 		    info->dvi_dual, info->max_tmds_clock);
623023ebf8b9SVille Syrjälä 
623123ebf8b9SVille Syrjälä 	drm_parse_hdmi_deep_color_info(connector, db);
623223ebf8b9SVille Syrjälä }
623323ebf8b9SVille Syrjälä 
62342869f599SPhilipp Zabel /*
62352869f599SPhilipp Zabel  * See EDID extension for head-mounted and specialized monitors, specified at:
62362869f599SPhilipp Zabel  * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
62372869f599SPhilipp Zabel  */
drm_parse_microsoft_vsdb(struct drm_connector * connector,const u8 * db)62382869f599SPhilipp Zabel static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
62392869f599SPhilipp Zabel 				     const u8 *db)
62402869f599SPhilipp Zabel {
62412869f599SPhilipp Zabel 	struct drm_display_info *info = &connector->display_info;
62422869f599SPhilipp Zabel 	u8 version = db[4];
62432869f599SPhilipp Zabel 	bool desktop_usage = db[5] & BIT(6);
62442869f599SPhilipp Zabel 
62452869f599SPhilipp Zabel 	/* Version 1 and 2 for HMDs, version 3 flags desktop usage explicitly */
62462869f599SPhilipp Zabel 	if (version == 1 || version == 2 || (version == 3 && !desktop_usage))
62472869f599SPhilipp Zabel 		info->non_desktop = true;
62482869f599SPhilipp Zabel 
624966d17ecdSJani Nikula 	drm_dbg_kms(connector->dev,
625066d17ecdSJani Nikula 		    "[CONNECTOR:%d:%s] HMD or specialized display VSDB version %u: 0x%02x\n",
625166d17ecdSJani Nikula 		    connector->base.id, connector->name, version, db[5]);
62522869f599SPhilipp Zabel }
62532869f599SPhilipp Zabel 
drm_parse_cea_ext(struct drm_connector * connector,const struct drm_edid * drm_edid)62541cea146aSVille Syrjälä static void drm_parse_cea_ext(struct drm_connector *connector,
6255e42192b4SJani Nikula 			      const struct drm_edid *drm_edid)
62563b11228bSJesse Barnes {
62571826750fSVille Syrjälä 	struct drm_display_info *info = &connector->display_info;
62588db73897SJani Nikula 	struct drm_edid_iter edid_iter;
6259dfc03125SJani Nikula 	const struct cea_db *db;
6260dfc03125SJani Nikula 	struct cea_db_iter iter;
62611cea146aSVille Syrjälä 	const u8 *edid_ext;
626261e05fdcSJani Nikula 	u64 y420cmdb_map = 0;
62631cea146aSVille Syrjälä 
6264bbded689SJani Nikula 	drm_edid_iter_begin(drm_edid, &edid_iter);
62658db73897SJani Nikula 	drm_edid_iter_for_each(edid_ext, &edid_iter) {
62668db73897SJani Nikula 		if (edid_ext[0] != CEA_EXT)
62678db73897SJani Nikula 			continue;
62681cea146aSVille Syrjälä 
62698db73897SJani Nikula 		if (!info->cea_rev)
62701cea146aSVille Syrjälä 			info->cea_rev = edid_ext[1];
62711cea146aSVille Syrjälä 
62728db73897SJani Nikula 		if (info->cea_rev != edid_ext[1])
6273e1e7bc48SJani Nikula 			drm_dbg_kms(connector->dev,
6274e1e7bc48SJani Nikula 				    "[CONNECTOR:%d:%s] CEA extension version mismatch %u != %u\n",
6275e1e7bc48SJani Nikula 				    connector->base.id, connector->name,
62768db73897SJani Nikula 				    info->cea_rev, edid_ext[1]);
62777344bad7SJani Nikula 
62788db73897SJani Nikula 		/* The existence of a CTA extension should imply RGB support */
62798db73897SJani Nikula 		info->color_formats = DRM_COLOR_FORMAT_RGB444;
62801cea146aSVille Syrjälä 		if (edid_ext[3] & EDID_CEA_YCRCB444)
6281c03d0b52SMaxime Ripard 			info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
62821cea146aSVille Syrjälä 		if (edid_ext[3] & EDID_CEA_YCRCB422)
6283c03d0b52SMaxime Ripard 			info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
62840374ffa5SJani Nikula 		if (edid_ext[3] & EDID_BASIC_AUDIO)
62850374ffa5SJani Nikula 			info->has_audio = true;
62860374ffa5SJani Nikula 
62877344bad7SJani Nikula 	}
62888db73897SJani Nikula 	drm_edid_iter_end(&edid_iter);
62891cea146aSVille Syrjälä 
62905e87b2e5SJani Nikula 	cea_db_iter_edid_begin(drm_edid, &iter);
6291dfc03125SJani Nikula 	cea_db_iter_for_each(db, &iter) {
6292dfc03125SJani Nikula 		/* FIXME: convert parsers to use struct cea_db */
6293dfc03125SJani Nikula 		const u8 *data = (const u8 *)db;
62941cea146aSVille Syrjälä 
629523ebf8b9SVille Syrjälä 		if (cea_db_is_hdmi_vsdb(db))
6296dfc03125SJani Nikula 			drm_parse_hdmi_vsdb_video(connector, data);
6297be982415SJani Nikula 		else if (cea_db_is_hdmi_forum_vsdb(db) ||
6298115fcf58SLee Shawn C 			 cea_db_is_hdmi_forum_scdb(db))
6299dfc03125SJani Nikula 			drm_parse_hdmi_forum_scds(connector, data);
6300be982415SJani Nikula 		else if (cea_db_is_microsoft_vsdb(db))
6301dfc03125SJani Nikula 			drm_parse_microsoft_vsdb(connector, data);
6302be982415SJani Nikula 		else if (cea_db_is_y420cmdb(db))
630361e05fdcSJani Nikula 			parse_cta_y420cmdb(connector, db, &y420cmdb_map);
6304c54e2e23SJani Nikula 		else if (cea_db_is_y420vdb(db))
6305c54e2e23SJani Nikula 			parse_cta_y420vdb(connector, db);
6306be982415SJani Nikula 		else if (cea_db_is_vcdb(db))
6307dfc03125SJani Nikula 			drm_parse_vcdb(connector, data);
6308be982415SJani Nikula 		else if (cea_db_is_hdmi_hdr_metadata_block(db))
6309dfc03125SJani Nikula 			drm_parse_hdr_metadata_block(connector, data);
6310c3292ab5SJani Nikula 		else if (cea_db_tag(db) == CTA_DB_VIDEO)
6311c3292ab5SJani Nikula 			parse_cta_vdb(connector, db);
63120374ffa5SJani Nikula 		else if (cea_db_tag(db) == CTA_DB_AUDIO)
63130374ffa5SJani Nikula 			info->has_audio = true;
63141cea146aSVille Syrjälä 	}
6315dfc03125SJani Nikula 	cea_db_iter_end(&iter);
631661e05fdcSJani Nikula 
631761e05fdcSJani Nikula 	if (y420cmdb_map)
631861e05fdcSJani Nikula 		update_cta_y420cmdb(connector, y420cmdb_map);
63191cea146aSVille Syrjälä }
63201cea146aSVille Syrjälä 
6321a1d11d1eSManasi Navare static
get_monitor_range(const struct detailed_timing * timing,void * c)6322c7943bb3SVille Syrjälä void get_monitor_range(const struct detailed_timing *timing, void *c)
6323a1d11d1eSManasi Navare {
6324c7943bb3SVille Syrjälä 	struct detailed_mode_closure *closure = c;
6325c7943bb3SVille Syrjälä 	struct drm_display_info *info = &closure->connector->display_info;
6326c7943bb3SVille Syrjälä 	struct drm_monitor_range_info *monitor_range = &info->monitor_range;
6327a1d11d1eSManasi Navare 	const struct detailed_non_pixel *data = &timing->data.other_data;
6328a1d11d1eSManasi Navare 	const struct detailed_data_monitor_range *range = &data->data.range;
6329c7943bb3SVille Syrjälä 	const struct edid *edid = closure->drm_edid->edid;
6330a1d11d1eSManasi Navare 
6331e379814bSJani Nikula 	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
6332a1d11d1eSManasi Navare 		return;
6333a1d11d1eSManasi Navare 
6334a1d11d1eSManasi Navare 	/*
633567d7469aSVille Syrjälä 	 * These limits are used to determine the VRR refresh
633667d7469aSVille Syrjälä 	 * rate range. Only the "range limits only" variant
633767d7469aSVille Syrjälä 	 * of the range descriptor seems to guarantee that
633867d7469aSVille Syrjälä 	 * any and all timings are accepted by the sink, as
633967d7469aSVille Syrjälä 	 * opposed to just timings conforming to the indicated
634067d7469aSVille Syrjälä 	 * formula (GTF/GTF2/CVT). Thus other variants of the
634167d7469aSVille Syrjälä 	 * range descriptor are not accepted here.
6342a1d11d1eSManasi Navare 	 */
6343a1d11d1eSManasi Navare 	if (range->flags != DRM_EDID_RANGE_LIMITS_ONLY_FLAG)
6344a1d11d1eSManasi Navare 		return;
6345a1d11d1eSManasi Navare 
6346a1d11d1eSManasi Navare 	monitor_range->min_vfreq = range->min_vfreq;
6347a1d11d1eSManasi Navare 	monitor_range->max_vfreq = range->max_vfreq;
6348c7943bb3SVille Syrjälä 
6349c7943bb3SVille Syrjälä 	if (edid->revision >= 4) {
6350c7943bb3SVille Syrjälä 		if (data->pad2 & DRM_EDID_RANGE_OFFSET_MIN_VFREQ)
6351c7943bb3SVille Syrjälä 			monitor_range->min_vfreq += 255;
6352c7943bb3SVille Syrjälä 		if (data->pad2 & DRM_EDID_RANGE_OFFSET_MAX_VFREQ)
6353c7943bb3SVille Syrjälä 			monitor_range->max_vfreq += 255;
6354c7943bb3SVille Syrjälä 	}
6355a1d11d1eSManasi Navare }
6356a1d11d1eSManasi Navare 
drm_get_monitor_range(struct drm_connector * connector,const struct drm_edid * drm_edid)6357e42192b4SJani Nikula static void drm_get_monitor_range(struct drm_connector *connector,
6358e42192b4SJani Nikula 				  const struct drm_edid *drm_edid)
6359a1d11d1eSManasi Navare {
6360c7943bb3SVille Syrjälä 	const struct drm_display_info *info = &connector->display_info;
6361c7943bb3SVille Syrjälä 	struct detailed_mode_closure closure = {
6362c7943bb3SVille Syrjälä 		.connector = connector,
6363c7943bb3SVille Syrjälä 		.drm_edid = drm_edid,
6364c7943bb3SVille Syrjälä 	};
6365a1d11d1eSManasi Navare 
6366dd3abfe4SVille Syrjälä 	if (drm_edid->edid->revision < 4)
6367ca2582c6SVille Syrjälä 		return;
6368ca2582c6SVille Syrjälä 
6369ca2582c6SVille Syrjälä 	if (!(drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ))
6370a1d11d1eSManasi Navare 		return;
6371a1d11d1eSManasi Navare 
6372c7943bb3SVille Syrjälä 	drm_for_each_detailed_block(drm_edid, get_monitor_range, &closure);
6373a1d11d1eSManasi Navare 
6374e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev,
6375e1e7bc48SJani Nikula 		    "[CONNECTOR:%d:%s] Supported Monitor Refresh rate range is %d Hz - %d Hz\n",
6376e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
6377e1e7bc48SJani Nikula 		    info->monitor_range.min_vfreq, info->monitor_range.max_vfreq);
6378a1d11d1eSManasi Navare }
6379a1d11d1eSManasi Navare 
drm_parse_vesa_mso_data(struct drm_connector * connector,const struct displayid_block * block)638018a9cbbeSJani Nikula static void drm_parse_vesa_mso_data(struct drm_connector *connector,
638118a9cbbeSJani Nikula 				    const struct displayid_block *block)
638218a9cbbeSJani Nikula {
638318a9cbbeSJani Nikula 	struct displayid_vesa_vendor_specific_block *vesa =
638418a9cbbeSJani Nikula 		(struct displayid_vesa_vendor_specific_block *)block;
638518a9cbbeSJani Nikula 	struct drm_display_info *info = &connector->display_info;
638618a9cbbeSJani Nikula 
638718a9cbbeSJani Nikula 	if (block->num_bytes < 3) {
638866d17ecdSJani Nikula 		drm_dbg_kms(connector->dev,
638966d17ecdSJani Nikula 			    "[CONNECTOR:%d:%s] Unexpected vendor block size %u\n",
639066d17ecdSJani Nikula 			    connector->base.id, connector->name, block->num_bytes);
639118a9cbbeSJani Nikula 		return;
639218a9cbbeSJani Nikula 	}
639318a9cbbeSJani Nikula 
639418a9cbbeSJani Nikula 	if (oui(vesa->oui[0], vesa->oui[1], vesa->oui[2]) != VESA_IEEE_OUI)
639518a9cbbeSJani Nikula 		return;
639618a9cbbeSJani Nikula 
639718a9cbbeSJani Nikula 	if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) {
639866d17ecdSJani Nikula 		drm_dbg_kms(connector->dev,
639966d17ecdSJani Nikula 			    "[CONNECTOR:%d:%s] Unexpected VESA vendor block size\n",
640066d17ecdSJani Nikula 			    connector->base.id, connector->name);
640118a9cbbeSJani Nikula 		return;
640218a9cbbeSJani Nikula 	}
640318a9cbbeSJani Nikula 
640418a9cbbeSJani Nikula 	switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) {
640518a9cbbeSJani Nikula 	default:
640666d17ecdSJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved MSO mode value\n",
640766d17ecdSJani Nikula 			    connector->base.id, connector->name);
640818a9cbbeSJani Nikula 		fallthrough;
640918a9cbbeSJani Nikula 	case 0:
641018a9cbbeSJani Nikula 		info->mso_stream_count = 0;
641118a9cbbeSJani Nikula 		break;
641218a9cbbeSJani Nikula 	case 1:
641318a9cbbeSJani Nikula 		info->mso_stream_count = 2; /* 2 or 4 links */
641418a9cbbeSJani Nikula 		break;
641518a9cbbeSJani Nikula 	case 2:
641618a9cbbeSJani Nikula 		info->mso_stream_count = 4; /* 4 links */
641718a9cbbeSJani Nikula 		break;
641818a9cbbeSJani Nikula 	}
641918a9cbbeSJani Nikula 
642018a9cbbeSJani Nikula 	if (!info->mso_stream_count) {
642118a9cbbeSJani Nikula 		info->mso_pixel_overlap = 0;
642218a9cbbeSJani Nikula 		return;
642318a9cbbeSJani Nikula 	}
642418a9cbbeSJani Nikula 
642518a9cbbeSJani Nikula 	info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso);
642618a9cbbeSJani Nikula 	if (info->mso_pixel_overlap > 8) {
642766d17ecdSJani Nikula 		drm_dbg_kms(connector->dev,
642866d17ecdSJani Nikula 			    "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n",
642966d17ecdSJani Nikula 			    connector->base.id, connector->name,
643018a9cbbeSJani Nikula 			    info->mso_pixel_overlap);
643118a9cbbeSJani Nikula 		info->mso_pixel_overlap = 8;
643218a9cbbeSJani Nikula 	}
643318a9cbbeSJani Nikula 
643466d17ecdSJani Nikula 	drm_dbg_kms(connector->dev,
643566d17ecdSJani Nikula 		    "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n",
643666d17ecdSJani Nikula 		    connector->base.id, connector->name,
643718a9cbbeSJani Nikula 		    info->mso_stream_count, info->mso_pixel_overlap);
643818a9cbbeSJani Nikula }
643918a9cbbeSJani Nikula 
drm_update_mso(struct drm_connector * connector,const struct drm_edid * drm_edid)6440e42192b4SJani Nikula static void drm_update_mso(struct drm_connector *connector,
6441e42192b4SJani Nikula 			   const struct drm_edid *drm_edid)
644218a9cbbeSJani Nikula {
644318a9cbbeSJani Nikula 	const struct displayid_block *block;
644418a9cbbeSJani Nikula 	struct displayid_iter iter;
644518a9cbbeSJani Nikula 
6446d9ba1b4cSJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter);
644718a9cbbeSJani Nikula 	displayid_iter_for_each(block, &iter) {
644818a9cbbeSJani Nikula 		if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
644918a9cbbeSJani Nikula 			drm_parse_vesa_mso_data(connector, block);
645018a9cbbeSJani Nikula 	}
645118a9cbbeSJani Nikula 	displayid_iter_end(&iter);
645218a9cbbeSJani Nikula }
645318a9cbbeSJani Nikula 
6454170178feSKeith Packard /* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
6455170178feSKeith Packard  * all of the values which would have been set from EDID
6456170178feSKeith Packard  */
drm_reset_display_info(struct drm_connector * connector)645702b16fbcSJani Nikula static void drm_reset_display_info(struct drm_connector *connector)
64581cea146aSVille Syrjälä {
64591cea146aSVille Syrjälä 	struct drm_display_info *info = &connector->display_info;
6460ebec9a7bSJesse Barnes 
6461170178feSKeith Packard 	info->width_mm = 0;
6462170178feSKeith Packard 	info->height_mm = 0;
6463170178feSKeith Packard 
6464170178feSKeith Packard 	info->bpc = 0;
6465170178feSKeith Packard 	info->color_formats = 0;
6466170178feSKeith Packard 	info->cea_rev = 0;
6467170178feSKeith Packard 	info->max_tmds_clock = 0;
6468170178feSKeith Packard 	info->dvi_dual = false;
6469a92d083dSLaurent Pinchart 	info->is_hdmi = false;
64700374ffa5SJani Nikula 	info->has_audio = false;
6471170178feSKeith Packard 	info->has_hdmi_infoframe = false;
64721581b2dfSVille Syrjälä 	info->rgb_quant_range_selectable = false;
64731f6b8eefSVille Syrjälä 	memset(&info->hdmi, 0, sizeof(info->hdmi));
6474170178feSKeith Packard 
647570c0b80dSMaxime Ripard 	info->edid_hdmi_rgb444_dc_modes = 0;
647670c0b80dSMaxime Ripard 	info->edid_hdmi_ycbcr444_dc_modes = 0;
647770c0b80dSMaxime Ripard 
6478170178feSKeith Packard 	info->non_desktop = 0;
6479a1d11d1eSManasi Navare 	memset(&info->monitor_range, 0, sizeof(info->monitor_range));
648082068edeSJouni Högander 	memset(&info->luminance_range, 0, sizeof(info->luminance_range));
648118a9cbbeSJani Nikula 
648218a9cbbeSJani Nikula 	info->mso_stream_count = 0;
648318a9cbbeSJani Nikula 	info->mso_pixel_overlap = 0;
6484aa193f7eSHamza Mahfooz 	info->max_dsc_bpp = 0;
6485c3292ab5SJani Nikula 
6486c3292ab5SJani Nikula 	kfree(info->vics);
6487c3292ab5SJani Nikula 	info->vics = NULL;
6488c3292ab5SJani Nikula 	info->vics_len = 0;
6489783dedc5SJani Nikula 
6490783dedc5SJani Nikula 	info->quirks = 0;
6491170178feSKeith Packard }
6492170178feSKeith Packard 
update_displayid_info(struct drm_connector * connector,const struct drm_edid * drm_edid)6493217a8c63SJani Nikula static void update_displayid_info(struct drm_connector *connector,
6494217a8c63SJani Nikula 				  const struct drm_edid *drm_edid)
6495217a8c63SJani Nikula {
6496217a8c63SJani Nikula 	struct drm_display_info *info = &connector->display_info;
6497217a8c63SJani Nikula 	const struct displayid_block *block;
6498217a8c63SJani Nikula 	struct displayid_iter iter;
6499217a8c63SJani Nikula 
6500217a8c63SJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter);
6501217a8c63SJani Nikula 	displayid_iter_for_each(block, &iter) {
6502217a8c63SJani Nikula 		if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
6503217a8c63SJani Nikula 		    (displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_VR ||
6504217a8c63SJani Nikula 		     displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_AR))
6505217a8c63SJani Nikula 			info->non_desktop = true;
6506217a8c63SJani Nikula 
6507217a8c63SJani Nikula 		/*
6508217a8c63SJani Nikula 		 * We're only interested in the base section here, no need to
6509217a8c63SJani Nikula 		 * iterate further.
6510217a8c63SJani Nikula 		 */
6511217a8c63SJani Nikula 		break;
6512217a8c63SJani Nikula 	}
6513217a8c63SJani Nikula 	displayid_iter_end(&iter);
6514217a8c63SJani Nikula }
6515217a8c63SJani Nikula 
update_display_info(struct drm_connector * connector,const struct drm_edid * drm_edid)6516783dedc5SJani Nikula static void update_display_info(struct drm_connector *connector,
6517e42192b4SJani Nikula 				const struct drm_edid *drm_edid)
6518170178feSKeith Packard {
6519170178feSKeith Packard 	struct drm_display_info *info = &connector->display_info;
652045ea02d1SJani Nikula 	const struct edid *edid;
6521170178feSKeith Packard 
65221f6b8eefSVille Syrjälä 	drm_reset_display_info(connector);
652345ea02d1SJani Nikula 	clear_eld(connector);
652445ea02d1SJani Nikula 
652545ea02d1SJani Nikula 	if (!drm_edid)
652645ea02d1SJani Nikula 		return;
652745ea02d1SJani Nikula 
652845ea02d1SJani Nikula 	edid = drm_edid->edid;
65291f6b8eefSVille Syrjälä 
6530783dedc5SJani Nikula 	info->quirks = edid_get_quirks(drm_edid);
6531783dedc5SJani Nikula 
65323b11228bSJesse Barnes 	info->width_mm = edid->width_cm * 10;
65333b11228bSJesse Barnes 	info->height_mm = edid->height_cm * 10;
65343b11228bSJesse Barnes 
6535e42192b4SJani Nikula 	drm_get_monitor_range(connector, drm_edid);
6536a1d11d1eSManasi Navare 
6537a988bc72SLars-Peter Clausen 	if (edid->revision < 3)
6538ce99534eSJani Nikula 		goto out;
65393b11228bSJesse Barnes 
65403b11228bSJesse Barnes 	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
6541ce99534eSJani Nikula 		goto out;
65423b11228bSJesse Barnes 
6543ecbd4912SMaxime Ripard 	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
6544e42192b4SJani Nikula 	drm_parse_cea_ext(connector, drm_edid);
6545d0c94692SMario Kleiner 
6546217a8c63SJani Nikula 	update_displayid_info(connector, drm_edid);
6547217a8c63SJani Nikula 
6548210a021dSMario Kleiner 	/*
6549210a021dSMario Kleiner 	 * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
6550210a021dSMario Kleiner 	 *
6551210a021dSMario Kleiner 	 * For such displays, the DFP spec 1.0, section 3.10 "EDID support"
6552210a021dSMario Kleiner 	 * tells us to assume 8 bpc color depth if the EDID doesn't have
6553210a021dSMario Kleiner 	 * extensions which tell otherwise.
6554210a021dSMario Kleiner 	 */
65553bde449fSVille Syrjälä 	if (info->bpc == 0 && edid->revision == 3 &&
65563bde449fSVille Syrjälä 	    edid->input & DRM_EDID_DIGITAL_DFP_1_X) {
6557210a021dSMario Kleiner 		info->bpc = 8;
6558e1e7bc48SJani Nikula 		drm_dbg_kms(connector->dev,
6559e1e7bc48SJani Nikula 			    "[CONNECTOR:%d:%s] Assigning DFP sink color depth as %d bpc.\n",
6560e1e7bc48SJani Nikula 			    connector->base.id, connector->name, info->bpc);
6561210a021dSMario Kleiner 	}
6562210a021dSMario Kleiner 
6563a988bc72SLars-Peter Clausen 	/* Only defined for 1.4 with digital displays */
6564a988bc72SLars-Peter Clausen 	if (edid->revision < 4)
6565ce99534eSJani Nikula 		goto out;
6566a988bc72SLars-Peter Clausen 
65673b11228bSJesse Barnes 	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
65683b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_6:
65693b11228bSJesse Barnes 		info->bpc = 6;
65703b11228bSJesse Barnes 		break;
65713b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_8:
65723b11228bSJesse Barnes 		info->bpc = 8;
65733b11228bSJesse Barnes 		break;
65743b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_10:
65753b11228bSJesse Barnes 		info->bpc = 10;
65763b11228bSJesse Barnes 		break;
65773b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_12:
65783b11228bSJesse Barnes 		info->bpc = 12;
65793b11228bSJesse Barnes 		break;
65803b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_14:
65813b11228bSJesse Barnes 		info->bpc = 14;
65823b11228bSJesse Barnes 		break;
65833b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_16:
65843b11228bSJesse Barnes 		info->bpc = 16;
65853b11228bSJesse Barnes 		break;
65863b11228bSJesse Barnes 	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
65873b11228bSJesse Barnes 	default:
65883b11228bSJesse Barnes 		info->bpc = 0;
65893b11228bSJesse Barnes 		break;
65903b11228bSJesse Barnes 	}
6591da05a5a7SJesse Barnes 
6592e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev,
6593e1e7bc48SJani Nikula 		    "[CONNECTOR:%d:%s] Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
6594e1e7bc48SJani Nikula 		    connector->base.id, connector->name, info->bpc);
6595d0c94692SMario Kleiner 
6596ee58808dSLars-Peter Clausen 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
6597c03d0b52SMaxime Ripard 		info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
6598ee58808dSLars-Peter Clausen 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
6599c03d0b52SMaxime Ripard 		info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
660018a9cbbeSJani Nikula 
6601e42192b4SJani Nikula 	drm_update_mso(connector, drm_edid);
660218a9cbbeSJani Nikula 
6603ce99534eSJani Nikula out:
6604783dedc5SJani Nikula 	if (info->quirks & EDID_QUIRK_NON_DESKTOP) {
660566d17ecdSJani Nikula 		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n",
660666d17ecdSJani Nikula 			    connector->base.id, connector->name,
6607ce99534eSJani Nikula 			    info->non_desktop ? " (redundant quirk)" : "");
6608ce99534eSJani Nikula 		info->non_desktop = true;
6609ce99534eSJani Nikula 	}
6610ce99534eSJani Nikula 
6611783dedc5SJani Nikula 	if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP)
6612aa193f7eSHamza Mahfooz 		info->max_dsc_bpp = 15;
661345ea02d1SJani Nikula 
661443bde505SJani Nikula 	if (info->quirks & EDID_QUIRK_FORCE_6BPC)
661543bde505SJani Nikula 		info->bpc = 6;
661643bde505SJani Nikula 
661743bde505SJani Nikula 	if (info->quirks & EDID_QUIRK_FORCE_8BPC)
661843bde505SJani Nikula 		info->bpc = 8;
661943bde505SJani Nikula 
662043bde505SJani Nikula 	if (info->quirks & EDID_QUIRK_FORCE_10BPC)
662143bde505SJani Nikula 		info->bpc = 10;
662243bde505SJani Nikula 
662343bde505SJani Nikula 	if (info->quirks & EDID_QUIRK_FORCE_12BPC)
662443bde505SJani Nikula 		info->bpc = 12;
662543bde505SJani Nikula 
662645ea02d1SJani Nikula 	/* Depends on info->cea_rev set by drm_parse_cea_ext() above */
662745ea02d1SJani Nikula 	drm_edid_to_eld(connector, drm_edid);
66283b11228bSJesse Barnes }
66293b11228bSJesse Barnes 
drm_mode_displayid_detailed(struct drm_device * dev,struct displayid_detailed_timings_1 * timings,bool type_7)6630a39ed680SDave Airlie static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
663180ecb5d7SYaroslav Bolyukin 							    struct displayid_detailed_timings_1 *timings,
663280ecb5d7SYaroslav Bolyukin 							    bool type_7)
6633a39ed680SDave Airlie {
6634a39ed680SDave Airlie 	struct drm_display_mode *mode;
6635a39ed680SDave Airlie 	unsigned pixel_clock = (timings->pixel_clock[0] |
6636a39ed680SDave Airlie 				(timings->pixel_clock[1] << 8) |
66376292b8efSVille Syrjälä 				(timings->pixel_clock[2] << 16)) + 1;
6638a39ed680SDave Airlie 	unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
6639a39ed680SDave Airlie 	unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
6640a39ed680SDave Airlie 	unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
6641a39ed680SDave Airlie 	unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
6642a39ed680SDave Airlie 	unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
6643a39ed680SDave Airlie 	unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1;
6644a39ed680SDave Airlie 	unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1;
6645a39ed680SDave Airlie 	unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
6646a39ed680SDave Airlie 	bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
6647a39ed680SDave Airlie 	bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
6648948de842SSuraj Upadhyay 
6649a39ed680SDave Airlie 	mode = drm_mode_create(dev);
6650a39ed680SDave Airlie 	if (!mode)
6651a39ed680SDave Airlie 		return NULL;
6652a39ed680SDave Airlie 
665380ecb5d7SYaroslav Bolyukin 	/* resolution is kHz for type VII, and 10 kHz for type I */
665480ecb5d7SYaroslav Bolyukin 	mode->clock = type_7 ? pixel_clock : pixel_clock * 10;
6655a39ed680SDave Airlie 	mode->hdisplay = hactive;
6656a39ed680SDave Airlie 	mode->hsync_start = mode->hdisplay + hsync;
6657a39ed680SDave Airlie 	mode->hsync_end = mode->hsync_start + hsync_width;
6658a39ed680SDave Airlie 	mode->htotal = mode->hdisplay + hblank;
6659a39ed680SDave Airlie 
6660a39ed680SDave Airlie 	mode->vdisplay = vactive;
6661a39ed680SDave Airlie 	mode->vsync_start = mode->vdisplay + vsync;
6662a39ed680SDave Airlie 	mode->vsync_end = mode->vsync_start + vsync_width;
6663a39ed680SDave Airlie 	mode->vtotal = mode->vdisplay + vblank;
6664a39ed680SDave Airlie 
6665a39ed680SDave Airlie 	mode->flags = 0;
6666a39ed680SDave Airlie 	mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
6667a39ed680SDave Airlie 	mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
6668a39ed680SDave Airlie 	mode->type = DRM_MODE_TYPE_DRIVER;
6669a39ed680SDave Airlie 
6670a39ed680SDave Airlie 	if (timings->flags & 0x80)
6671a39ed680SDave Airlie 		mode->type |= DRM_MODE_TYPE_PREFERRED;
6672a39ed680SDave Airlie 	drm_mode_set_name(mode);
6673a39ed680SDave Airlie 
6674a39ed680SDave Airlie 	return mode;
6675a39ed680SDave Airlie }
6676a39ed680SDave Airlie 
add_displayid_detailed_1_modes(struct drm_connector * connector,const struct displayid_block * block)6677a39ed680SDave Airlie static int add_displayid_detailed_1_modes(struct drm_connector *connector,
667843d16d84SJani Nikula 					  const struct displayid_block *block)
6679a39ed680SDave Airlie {
6680a39ed680SDave Airlie 	struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block;
6681a39ed680SDave Airlie 	int i;
6682a39ed680SDave Airlie 	int num_timings;
6683a39ed680SDave Airlie 	struct drm_display_mode *newmode;
6684a39ed680SDave Airlie 	int num_modes = 0;
668580ecb5d7SYaroslav Bolyukin 	bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING;
6686a39ed680SDave Airlie 	/* blocks must be multiple of 20 bytes length */
6687a39ed680SDave Airlie 	if (block->num_bytes % 20)
6688a39ed680SDave Airlie 		return 0;
6689a39ed680SDave Airlie 
6690a39ed680SDave Airlie 	num_timings = block->num_bytes / 20;
6691a39ed680SDave Airlie 	for (i = 0; i < num_timings; i++) {
6692a39ed680SDave Airlie 		struct displayid_detailed_timings_1 *timings = &det->timings[i];
6693a39ed680SDave Airlie 
669480ecb5d7SYaroslav Bolyukin 		newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7);
6695a39ed680SDave Airlie 		if (!newmode)
6696a39ed680SDave Airlie 			continue;
6697a39ed680SDave Airlie 
6698a39ed680SDave Airlie 		drm_mode_probed_add(connector, newmode);
6699a39ed680SDave Airlie 		num_modes++;
6700a39ed680SDave Airlie 	}
6701a39ed680SDave Airlie 	return num_modes;
6702a39ed680SDave Airlie }
6703a39ed680SDave Airlie 
add_displayid_detailed_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)6704a39ed680SDave Airlie static int add_displayid_detailed_modes(struct drm_connector *connector,
670540f71f5bSJani Nikula 					const struct drm_edid *drm_edid)
6706a39ed680SDave Airlie {
670743d16d84SJani Nikula 	const struct displayid_block *block;
67085ef88dc5SJani Nikula 	struct displayid_iter iter;
6709a39ed680SDave Airlie 	int num_modes = 0;
6710a39ed680SDave Airlie 
6711d9ba1b4cSJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter);
67125ef88dc5SJani Nikula 	displayid_iter_for_each(block, &iter) {
671380ecb5d7SYaroslav Bolyukin 		if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
671480ecb5d7SYaroslav Bolyukin 		    block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
6715a39ed680SDave Airlie 			num_modes += add_displayid_detailed_1_modes(connector, block);
6716a39ed680SDave Airlie 	}
67175ef88dc5SJani Nikula 	displayid_iter_end(&iter);
67187f261afdSVille Syrjälä 
6719a39ed680SDave Airlie 	return num_modes;
6720a39ed680SDave Airlie }
6721a39ed680SDave Airlie 
_drm_edid_connector_add_modes(struct drm_connector * connector,const struct drm_edid * drm_edid)6722e8b1f0d4SJani Nikula static int _drm_edid_connector_add_modes(struct drm_connector *connector,
672322a27e05SJani Nikula 					 const struct drm_edid *drm_edid)
6724f453ba04SDave Airlie {
672543bde505SJani Nikula 	const struct drm_display_info *info = &connector->display_info;
6726f453ba04SDave Airlie 	int num_modes = 0;
6727f453ba04SDave Airlie 
672845ea02d1SJani Nikula 	if (!drm_edid)
672945ea02d1SJani Nikula 		return 0;
673058304630SJani Nikula 
67310f0f8708SShashank Sharma 	/*
6732c867df70SAdam Jackson 	 * EDID spec says modes should be preferred in this order:
6733c867df70SAdam Jackson 	 * - preferred detailed mode
6734c867df70SAdam Jackson 	 * - other detailed modes from base block
6735c867df70SAdam Jackson 	 * - detailed modes from extension blocks
6736c867df70SAdam Jackson 	 * - CVT 3-byte code modes
6737c867df70SAdam Jackson 	 * - standard timing codes
6738c867df70SAdam Jackson 	 * - established timing codes
6739c867df70SAdam Jackson 	 * - modes inferred from GTF or CVT range information
6740c867df70SAdam Jackson 	 *
674113931579SAdam Jackson 	 * We get this pretty much right.
6742c867df70SAdam Jackson 	 *
6743c867df70SAdam Jackson 	 * XXX order for additional mode types in extension blocks?
6744c867df70SAdam Jackson 	 */
67454959b693SJani Nikula 	num_modes += add_detailed_modes(connector, drm_edid);
674640f71f5bSJani Nikula 	num_modes += add_cvt_modes(connector, drm_edid);
674740f71f5bSJani Nikula 	num_modes += add_standard_modes(connector, drm_edid);
674840f71f5bSJani Nikula 	num_modes += add_established_modes(connector, drm_edid);
674940f71f5bSJani Nikula 	num_modes += add_cea_modes(connector, drm_edid);
675040f71f5bSJani Nikula 	num_modes += add_alternate_cea_modes(connector, drm_edid);
675140f71f5bSJani Nikula 	num_modes += add_displayid_detailed_modes(connector, drm_edid);
6752afd4429eSVille Syrjälä 	if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ)
675340f71f5bSJani Nikula 		num_modes += add_inferred_modes(connector, drm_edid);
6754f453ba04SDave Airlie 
6755783dedc5SJani Nikula 	if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
67564959b693SJani Nikula 		edid_fixup_preferred(connector);
6757f453ba04SDave Airlie 
6758f453ba04SDave Airlie 	return num_modes;
6759f453ba04SDave Airlie }
6760f40ab034SJani Nikula 
6761a819451eSJani Nikula static void _drm_update_tile_info(struct drm_connector *connector,
6762a819451eSJani Nikula 				  const struct drm_edid *drm_edid);
6763a819451eSJani Nikula 
_drm_edid_connector_property_update(struct drm_connector * connector,const struct drm_edid * drm_edid)6764b71c0aaaSJani Nikula static int _drm_edid_connector_property_update(struct drm_connector *connector,
6765a819451eSJani Nikula 					       const struct drm_edid *drm_edid)
6766a819451eSJani Nikula {
6767a819451eSJani Nikula 	struct drm_device *dev = connector->dev;
6768a819451eSJani Nikula 	int ret;
6769a819451eSJani Nikula 
6770a819451eSJani Nikula 	if (connector->edid_blob_ptr) {
6771a819451eSJani Nikula 		const struct edid *old_edid = connector->edid_blob_ptr->data;
6772a819451eSJani Nikula 
6773a819451eSJani Nikula 		if (old_edid) {
6774a819451eSJani Nikula 			if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) {
6775f999b37eSJani Nikula 				connector->epoch_counter++;
6776f999b37eSJani Nikula 				drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n",
6777f999b37eSJani Nikula 					    connector->base.id, connector->name,
6778a819451eSJani Nikula 					    connector->epoch_counter);
6779a819451eSJani Nikula 			}
6780a819451eSJani Nikula 		}
6781a819451eSJani Nikula 	}
6782a819451eSJani Nikula 
6783a819451eSJani Nikula 	ret = drm_property_replace_global_blob(dev,
6784a819451eSJani Nikula 					       &connector->edid_blob_ptr,
6785a819451eSJani Nikula 					       drm_edid ? drm_edid->size : 0,
6786a819451eSJani Nikula 					       drm_edid ? drm_edid->edid : NULL,
6787a819451eSJani Nikula 					       &connector->base,
6788a819451eSJani Nikula 					       dev->mode_config.edid_property);
6789f999b37eSJani Nikula 	if (ret) {
6790f999b37eSJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID property update failed (%d)\n",
6791f999b37eSJani Nikula 			    connector->base.id, connector->name, ret);
6792f999b37eSJani Nikula 		goto out;
6793f999b37eSJani Nikula 	}
6794f999b37eSJani Nikula 
6795f999b37eSJani Nikula 	ret = drm_object_property_set_value(&connector->base,
6796f999b37eSJani Nikula 					    dev->mode_config.non_desktop_property,
6797f999b37eSJani Nikula 					    connector->display_info.non_desktop);
6798f999b37eSJani Nikula 	if (ret) {
6799f999b37eSJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Non-desktop property update failed (%d)\n",
6800f999b37eSJani Nikula 			    connector->base.id, connector->name, ret);
6801f999b37eSJani Nikula 		goto out;
6802f999b37eSJani Nikula 	}
6803f999b37eSJani Nikula 
6804f999b37eSJani Nikula 	ret = drm_connector_set_tile_property(connector);
6805f999b37eSJani Nikula 	if (ret) {
6806f999b37eSJani Nikula 		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Tile property update failed (%d)\n",
6807f999b37eSJani Nikula 			    connector->base.id, connector->name, ret);
6808f999b37eSJani Nikula 		goto out;
6809f999b37eSJani Nikula 	}
6810f999b37eSJani Nikula 
6811f999b37eSJani Nikula out:
6812a819451eSJani Nikula 	return ret;
6813a819451eSJani Nikula }
681402b16fbcSJani Nikula 
681502b16fbcSJani Nikula /**
6816b71c0aaaSJani Nikula  * drm_edid_connector_update - Update connector information from EDID
6817b71c0aaaSJani Nikula  * @connector: Connector
6818b71c0aaaSJani Nikula  * @drm_edid: EDID
6819b71c0aaaSJani Nikula  *
6820c533b516SJani Nikula  * Update the connector display info, ELD, HDR metadata, relevant properties,
6821c533b516SJani Nikula  * etc. from the passed in EDID.
6822b71c0aaaSJani Nikula  *
6823b71c0aaaSJani Nikula  * If EDID is NULL, reset the information.
6824b71c0aaaSJani Nikula  *
6825c533b516SJani Nikula  * Must be called before calling drm_edid_connector_add_modes().
6826c533b516SJani Nikula  *
6827c533b516SJani Nikula  * Return: 0 on success, negative error on errors.
6828b71c0aaaSJani Nikula  */
drm_edid_connector_update(struct drm_connector * connector,const struct drm_edid * drm_edid)6829b71c0aaaSJani Nikula int drm_edid_connector_update(struct drm_connector *connector,
6830b71c0aaaSJani Nikula 			      const struct drm_edid *drm_edid)
6831b71c0aaaSJani Nikula {
6832e8b1f0d4SJani Nikula 	update_display_info(connector, drm_edid);
6833e8b1f0d4SJani Nikula 
6834b71c0aaaSJani Nikula 	_drm_update_tile_info(connector, drm_edid);
6835b71c0aaaSJani Nikula 
6836c533b516SJani Nikula 	return _drm_edid_connector_property_update(connector, drm_edid);
6837c533b516SJani Nikula }
6838c533b516SJani Nikula EXPORT_SYMBOL(drm_edid_connector_update);
6839c533b516SJani Nikula 
6840c533b516SJani Nikula /**
6841c533b516SJani Nikula  * drm_edid_connector_add_modes - Update probed modes from the EDID property
6842c533b516SJani Nikula  * @connector: Connector
6843c533b516SJani Nikula  *
6844c533b516SJani Nikula  * Add the modes from the previously updated EDID property to the connector
6845c533b516SJani Nikula  * probed modes list.
6846c533b516SJani Nikula  *
6847c533b516SJani Nikula  * drm_edid_connector_update() must have been called before this to update the
6848c533b516SJani Nikula  * EDID property.
6849c533b516SJani Nikula  *
6850c533b516SJani Nikula  * Return: The number of modes added, or 0 if we couldn't find any.
6851c533b516SJani Nikula  */
drm_edid_connector_add_modes(struct drm_connector * connector)6852c533b516SJani Nikula int drm_edid_connector_add_modes(struct drm_connector *connector)
6853c533b516SJani Nikula {
6854c533b516SJani Nikula 	const struct drm_edid *drm_edid = NULL;
6855c533b516SJani Nikula 	int count;
6856c533b516SJani Nikula 
6857c533b516SJani Nikula 	if (connector->edid_blob_ptr)
6858c533b516SJani Nikula 		drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data,
6859c533b516SJani Nikula 					  connector->edid_blob_ptr->length);
6860c533b516SJani Nikula 
6861c533b516SJani Nikula 	count = _drm_edid_connector_add_modes(connector, drm_edid);
6862c533b516SJani Nikula 
6863c533b516SJani Nikula 	drm_edid_free(drm_edid);
6864b71c0aaaSJani Nikula 
6865b71c0aaaSJani Nikula 	return count;
6866b71c0aaaSJani Nikula }
6867c533b516SJani Nikula EXPORT_SYMBOL(drm_edid_connector_add_modes);
6868b71c0aaaSJani Nikula 
6869b71c0aaaSJani Nikula /**
687002b16fbcSJani Nikula  * drm_connector_update_edid_property - update the edid property of a connector
687102b16fbcSJani Nikula  * @connector: drm connector
687202b16fbcSJani Nikula  * @edid: new value of the edid property
687302b16fbcSJani Nikula  *
687402b16fbcSJani Nikula  * This function creates a new blob modeset object and assigns its id to the
687502b16fbcSJani Nikula  * connector's edid property.
687602b16fbcSJani Nikula  * Since we also parse tile information from EDID's displayID block, we also
687702b16fbcSJani Nikula  * set the connector's tile property here. See drm_connector_set_tile_property()
687802b16fbcSJani Nikula  * for more details.
687902b16fbcSJani Nikula  *
6880b71c0aaaSJani Nikula  * This function is deprecated. Use drm_edid_connector_update() instead.
6881b71c0aaaSJani Nikula  *
688202b16fbcSJani Nikula  * Returns:
688302b16fbcSJani Nikula  * Zero on success, negative errno on failure.
688402b16fbcSJani Nikula  */
drm_connector_update_edid_property(struct drm_connector * connector,const struct edid * edid)688502b16fbcSJani Nikula int drm_connector_update_edid_property(struct drm_connector *connector,
688602b16fbcSJani Nikula 				       const struct edid *edid)
688702b16fbcSJani Nikula {
6888a819451eSJani Nikula 	struct drm_edid drm_edid;
688902b16fbcSJani Nikula 
6890b494d628SJani Nikula 	return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid));
689102b16fbcSJani Nikula }
689202b16fbcSJani Nikula EXPORT_SYMBOL(drm_connector_update_edid_property);
689302b16fbcSJani Nikula 
6894f40ab034SJani Nikula /**
6895f40ab034SJani Nikula  * drm_add_edid_modes - add modes from EDID data, if available
6896f40ab034SJani Nikula  * @connector: connector we're probing
6897f40ab034SJani Nikula  * @edid: EDID data
6898f40ab034SJani Nikula  *
6899f40ab034SJani Nikula  * Add the specified modes to the connector's mode list. Also fills out the
6900f40ab034SJani Nikula  * &drm_display_info structure and ELD in @connector with any information which
6901f40ab034SJani Nikula  * can be derived from the edid.
6902f40ab034SJani Nikula  *
6903c533b516SJani Nikula  * This function is deprecated. Use drm_edid_connector_add_modes() instead.
6904b71c0aaaSJani Nikula  *
6905f40ab034SJani Nikula  * Return: The number of modes added or 0 if we couldn't find any.
6906f40ab034SJani Nikula  */
drm_add_edid_modes(struct drm_connector * connector,struct edid * edid)6907f40ab034SJani Nikula int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
6908f40ab034SJani Nikula {
6909e8b1f0d4SJani Nikula 	struct drm_edid _drm_edid;
6910e8b1f0d4SJani Nikula 	const struct drm_edid *drm_edid;
691122a27e05SJani Nikula 
6912f40ab034SJani Nikula 	if (edid && !drm_edid_is_valid(edid)) {
691366d17ecdSJani Nikula 		drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n",
691466d17ecdSJani Nikula 			 connector->base.id, connector->name);
6915f40ab034SJani Nikula 		edid = NULL;
6916f40ab034SJani Nikula 	}
6917f40ab034SJani Nikula 
6918e8b1f0d4SJani Nikula 	drm_edid = drm_edid_legacy_init(&_drm_edid, edid);
6919e8b1f0d4SJani Nikula 
6920e8b1f0d4SJani Nikula 	update_display_info(connector, drm_edid);
6921e8b1f0d4SJani Nikula 
6922e8b1f0d4SJani Nikula 	return _drm_edid_connector_add_modes(connector, drm_edid);
6923f40ab034SJani Nikula }
6924f453ba04SDave Airlie EXPORT_SYMBOL(drm_add_edid_modes);
6925f0fda0a4SZhao Yakui 
6926f0fda0a4SZhao Yakui /**
6927f0fda0a4SZhao Yakui  * drm_add_modes_noedid - add modes for the connectors without EDID
6928f0fda0a4SZhao Yakui  * @connector: connector we're probing
6929f0fda0a4SZhao Yakui  * @hdisplay: the horizontal display limit
6930f0fda0a4SZhao Yakui  * @vdisplay: the vertical display limit
6931f0fda0a4SZhao Yakui  *
6932f0fda0a4SZhao Yakui  * Add the specified modes to the connector's mode list. Only when the
6933f0fda0a4SZhao Yakui  * hdisplay/vdisplay is not beyond the given limit, it will be added.
6934f0fda0a4SZhao Yakui  *
6935db6cf833SThierry Reding  * Return: The number of modes added or 0 if we couldn't find any.
6936f0fda0a4SZhao Yakui  */
drm_add_modes_noedid(struct drm_connector * connector,int hdisplay,int vdisplay)6937f0fda0a4SZhao Yakui int drm_add_modes_noedid(struct drm_connector *connector,
6938f0fda0a4SZhao Yakui 			int hdisplay, int vdisplay)
6939f0fda0a4SZhao Yakui {
6940f0fda0a4SZhao Yakui 	int i, count, num_modes = 0;
6941b1f559ecSChris Wilson 	struct drm_display_mode *mode;
6942f0fda0a4SZhao Yakui 	struct drm_device *dev = connector->dev;
6943f0fda0a4SZhao Yakui 
6944fbb40b28SDaniel Vetter 	count = ARRAY_SIZE(drm_dmt_modes);
6945f0fda0a4SZhao Yakui 	if (hdisplay < 0)
6946f0fda0a4SZhao Yakui 		hdisplay = 0;
6947f0fda0a4SZhao Yakui 	if (vdisplay < 0)
6948f0fda0a4SZhao Yakui 		vdisplay = 0;
6949f0fda0a4SZhao Yakui 
6950f0fda0a4SZhao Yakui 	for (i = 0; i < count; i++) {
6951b1f559ecSChris Wilson 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
6952948de842SSuraj Upadhyay 
6953f0fda0a4SZhao Yakui 		if (hdisplay && vdisplay) {
6954f0fda0a4SZhao Yakui 			/*
6955f0fda0a4SZhao Yakui 			 * Only when two are valid, they will be used to check
6956f0fda0a4SZhao Yakui 			 * whether the mode should be added to the mode list of
6957f0fda0a4SZhao Yakui 			 * the connector.
6958f0fda0a4SZhao Yakui 			 */
6959f0fda0a4SZhao Yakui 			if (ptr->hdisplay > hdisplay ||
6960f0fda0a4SZhao Yakui 					ptr->vdisplay > vdisplay)
6961f0fda0a4SZhao Yakui 				continue;
6962f0fda0a4SZhao Yakui 		}
6963f985dedbSAdam Jackson 		if (drm_mode_vrefresh(ptr) > 61)
6964f985dedbSAdam Jackson 			continue;
6965f0fda0a4SZhao Yakui 		mode = drm_mode_duplicate(dev, ptr);
6966f0fda0a4SZhao Yakui 		if (mode) {
6967f0fda0a4SZhao Yakui 			drm_mode_probed_add(connector, mode);
6968f0fda0a4SZhao Yakui 			num_modes++;
6969f0fda0a4SZhao Yakui 		}
6970f0fda0a4SZhao Yakui 	}
6971f0fda0a4SZhao Yakui 	return num_modes;
6972f0fda0a4SZhao Yakui }
6973f0fda0a4SZhao Yakui EXPORT_SYMBOL(drm_add_modes_noedid);
697410a85120SThierry Reding 
6975db6cf833SThierry Reding /**
6976db6cf833SThierry Reding  * drm_set_preferred_mode - Sets the preferred mode of a connector
6977db6cf833SThierry Reding  * @connector: connector whose mode list should be processed
6978db6cf833SThierry Reding  * @hpref: horizontal resolution of preferred mode
6979db6cf833SThierry Reding  * @vpref: vertical resolution of preferred mode
6980db6cf833SThierry Reding  *
6981db6cf833SThierry Reding  * Marks a mode as preferred if it matches the resolution specified by @hpref
6982db6cf833SThierry Reding  * and @vpref.
6983db6cf833SThierry Reding  */
drm_set_preferred_mode(struct drm_connector * connector,int hpref,int vpref)69843cf70dafSGerd Hoffmann void drm_set_preferred_mode(struct drm_connector *connector,
69853cf70dafSGerd Hoffmann 			   int hpref, int vpref)
69863cf70dafSGerd Hoffmann {
69873cf70dafSGerd Hoffmann 	struct drm_display_mode *mode;
69883cf70dafSGerd Hoffmann 
69893cf70dafSGerd Hoffmann 	list_for_each_entry(mode, &connector->probed_modes, head) {
69909d3de138SDaniel Vetter 		if (mode->hdisplay == hpref &&
69919d3de138SDaniel Vetter 		    mode->vdisplay == vpref)
69923cf70dafSGerd Hoffmann 			mode->type |= DRM_MODE_TYPE_PREFERRED;
69933cf70dafSGerd Hoffmann 	}
69943cf70dafSGerd Hoffmann }
69953cf70dafSGerd Hoffmann EXPORT_SYMBOL(drm_set_preferred_mode);
69963cf70dafSGerd Hoffmann 
is_hdmi2_sink(const struct drm_connector * connector)6997192a3aa0SLaurent Pinchart static bool is_hdmi2_sink(const struct drm_connector *connector)
699813d0add3SVille Syrjälä {
699913d0add3SVille Syrjälä 	/*
700013d0add3SVille Syrjälä 	 * FIXME: sil-sii8620 doesn't have a connector around when
700113d0add3SVille Syrjälä 	 * we need one, so we have to be prepared for a NULL connector.
700213d0add3SVille Syrjälä 	 */
700313d0add3SVille Syrjälä 	if (!connector)
700413d0add3SVille Syrjälä 		return true;
700513d0add3SVille Syrjälä 
700613d0add3SVille Syrjälä 	return connector->display_info.hdmi.scdc.supported ||
7007c03d0b52SMaxime Ripard 		connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR420;
700813d0add3SVille Syrjälä }
700913d0add3SVille Syrjälä 
drm_mode_hdmi_vic(const struct drm_connector * connector,const struct drm_display_mode * mode)7010192a3aa0SLaurent Pinchart static u8 drm_mode_hdmi_vic(const struct drm_connector *connector,
7011949561ebSVille Syrjälä 			    const struct drm_display_mode *mode)
7012949561ebSVille Syrjälä {
7013949561ebSVille Syrjälä 	bool has_hdmi_infoframe = connector ?
7014949561ebSVille Syrjälä 		connector->display_info.has_hdmi_infoframe : false;
7015949561ebSVille Syrjälä 
7016949561ebSVille Syrjälä 	if (!has_hdmi_infoframe)
7017949561ebSVille Syrjälä 		return 0;
7018949561ebSVille Syrjälä 
7019949561ebSVille Syrjälä 	/* No HDMI VIC when signalling 3D video format */
7020949561ebSVille Syrjälä 	if (mode->flags & DRM_MODE_FLAG_3D_MASK)
7021949561ebSVille Syrjälä 		return 0;
7022949561ebSVille Syrjälä 
7023949561ebSVille Syrjälä 	return drm_match_hdmi_mode(mode);
7024949561ebSVille Syrjälä }
7025949561ebSVille Syrjälä 
drm_mode_cea_vic(const struct drm_connector * connector,const struct drm_display_mode * mode)7026192a3aa0SLaurent Pinchart static u8 drm_mode_cea_vic(const struct drm_connector *connector,
7027cfd6f8c3SVille Syrjälä 			   const struct drm_display_mode *mode)
7028cfd6f8c3SVille Syrjälä {
7029cfd6f8c3SVille Syrjälä 	/*
7030cfd6f8c3SVille Syrjälä 	 * HDMI spec says if a mode is found in HDMI 1.4b 4K modes
7031cfd6f8c3SVille Syrjälä 	 * we should send its VIC in vendor infoframes, else send the
7032cfd6f8c3SVille Syrjälä 	 * VIC in AVI infoframes. Lets check if this mode is present in
7033cfd6f8c3SVille Syrjälä 	 * HDMI 1.4b 4K modes
7034cfd6f8c3SVille Syrjälä 	 */
7035949561ebSVille Syrjälä 	if (drm_mode_hdmi_vic(connector, mode))
7036cfd6f8c3SVille Syrjälä 		return 0;
7037cfd6f8c3SVille Syrjälä 
70381cbc1f0dSJani Nikula 	return drm_match_cea_mode(mode);
70391cbc1f0dSJani Nikula }
7040cfd6f8c3SVille Syrjälä 
7041cfd6f8c3SVille Syrjälä /*
70421cbc1f0dSJani Nikula  * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that
70431cbc1f0dSJani Nikula  * conform to HDMI 1.4.
70441cbc1f0dSJani Nikula  *
70451cbc1f0dSJani Nikula  * HDMI 1.4 (CTA-861-D) VIC range: [1..64]
70461cbc1f0dSJani Nikula  * HDMI 2.0 (CTA-861-F) VIC range: [1..107]
70474ed29f39SJani Nikula  *
70484ed29f39SJani Nikula  * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI
70494ed29f39SJani Nikula  * version.
7050cfd6f8c3SVille Syrjälä  */
vic_for_avi_infoframe(const struct drm_connector * connector,u8 vic)70511cbc1f0dSJani Nikula static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic)
70521cbc1f0dSJani Nikula {
70534ed29f39SJani Nikula 	if (!is_hdmi2_sink(connector) && vic > 64 &&
70544ed29f39SJani Nikula 	    !cta_vdb_has_vic(connector, vic))
7055cfd6f8c3SVille Syrjälä 		return 0;
7056cfd6f8c3SVille Syrjälä 
7057cfd6f8c3SVille Syrjälä 	return vic;
7058cfd6f8c3SVille Syrjälä }
7059cfd6f8c3SVille Syrjälä 
706010a85120SThierry Reding /**
706110a85120SThierry Reding  * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
706210a85120SThierry Reding  *                                              data from a DRM display mode
706310a85120SThierry Reding  * @frame: HDMI AVI infoframe
706413d0add3SVille Syrjälä  * @connector: the connector
706510a85120SThierry Reding  * @mode: DRM display mode
706610a85120SThierry Reding  *
7067db6cf833SThierry Reding  * Return: 0 on success or a negative error code on failure.
706810a85120SThierry Reding  */
706910a85120SThierry Reding int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe * frame,const struct drm_connector * connector,const struct drm_display_mode * mode)707010a85120SThierry Reding drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
7071192a3aa0SLaurent Pinchart 					 const struct drm_connector *connector,
707213d0add3SVille Syrjälä 					 const struct drm_display_mode *mode)
707310a85120SThierry Reding {
7074a9c266c2SVille Syrjälä 	enum hdmi_picture_aspect picture_aspect;
7075d2b43473SWayne Lin 	u8 vic, hdmi_vic;
707610a85120SThierry Reding 
707710a85120SThierry Reding 	if (!frame || !mode)
707810a85120SThierry Reding 		return -EINVAL;
707910a85120SThierry Reding 
70805ee0caf1SLaurent Pinchart 	hdmi_avi_infoframe_init(frame);
708110a85120SThierry Reding 
7082bf02db99SDamien Lespiau 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
7083bf02db99SDamien Lespiau 		frame->pixel_repeat = 1;
7084bf02db99SDamien Lespiau 
7085d2b43473SWayne Lin 	vic = drm_mode_cea_vic(connector, mode);
7086d2b43473SWayne Lin 	hdmi_vic = drm_mode_hdmi_vic(connector, mode);
70870c1f528cSShashank Sharma 
708810a85120SThierry Reding 	frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
70890967e6a5SVandana Kannan 
709069ab6d35SVandana Kannan 	/*
709150525c33SStanislav Lisovskiy 	 * As some drivers don't support atomic, we can't use connector state.
709250525c33SStanislav Lisovskiy 	 * So just initialize the frame with default values, just the same way
709350525c33SStanislav Lisovskiy 	 * as it's done with other properties here.
709450525c33SStanislav Lisovskiy 	 */
709550525c33SStanislav Lisovskiy 	frame->content_type = HDMI_CONTENT_TYPE_GRAPHICS;
709650525c33SStanislav Lisovskiy 	frame->itc = 0;
709750525c33SStanislav Lisovskiy 
709850525c33SStanislav Lisovskiy 	/*
709969ab6d35SVandana Kannan 	 * Populate picture aspect ratio from either
7100d2b43473SWayne Lin 	 * user input (if specified) or from the CEA/HDMI mode lists.
710169ab6d35SVandana Kannan 	 */
7102a9c266c2SVille Syrjälä 	picture_aspect = mode->picture_aspect_ratio;
7103d2b43473SWayne Lin 	if (picture_aspect == HDMI_PICTURE_ASPECT_NONE) {
7104d2b43473SWayne Lin 		if (vic)
7105d2b43473SWayne Lin 			picture_aspect = drm_get_cea_aspect_ratio(vic);
7106d2b43473SWayne Lin 		else if (hdmi_vic)
7107d2b43473SWayne Lin 			picture_aspect = drm_get_hdmi_aspect_ratio(hdmi_vic);
7108d2b43473SWayne Lin 	}
71090967e6a5SVandana Kannan 
7110a9c266c2SVille Syrjälä 	/*
7111a9c266c2SVille Syrjälä 	 * The infoframe can't convey anything but none, 4:3
7112a9c266c2SVille Syrjälä 	 * and 16:9, so if the user has asked for anything else
7113a9c266c2SVille Syrjälä 	 * we can only satisfy it by specifying the right VIC.
7114a9c266c2SVille Syrjälä 	 */
7115a9c266c2SVille Syrjälä 	if (picture_aspect > HDMI_PICTURE_ASPECT_16_9) {
7116d2b43473SWayne Lin 		if (vic) {
7117d2b43473SWayne Lin 			if (picture_aspect != drm_get_cea_aspect_ratio(vic))
7118a9c266c2SVille Syrjälä 				return -EINVAL;
7119d2b43473SWayne Lin 		} else if (hdmi_vic) {
7120d2b43473SWayne Lin 			if (picture_aspect != drm_get_hdmi_aspect_ratio(hdmi_vic))
7121d2b43473SWayne Lin 				return -EINVAL;
7122d2b43473SWayne Lin 		} else {
7123d2b43473SWayne Lin 			return -EINVAL;
7124d2b43473SWayne Lin 		}
7125d2b43473SWayne Lin 
7126a9c266c2SVille Syrjälä 		picture_aspect = HDMI_PICTURE_ASPECT_NONE;
7127a9c266c2SVille Syrjälä 	}
7128a9c266c2SVille Syrjälä 
71291cbc1f0dSJani Nikula 	frame->video_code = vic_for_avi_infoframe(connector, vic);
7130a9c266c2SVille Syrjälä 	frame->picture_aspect = picture_aspect;
713110a85120SThierry Reding 	frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
713224d01805SDaniel Drake 	frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
713310a85120SThierry Reding 
713410a85120SThierry Reding 	return 0;
713510a85120SThierry Reding }
713610a85120SThierry Reding EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
713783dd0008SLespiau, Damien 
7138a2ce26f8SVille Syrjälä /**
7139a2ce26f8SVille Syrjälä  * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
7140a2ce26f8SVille Syrjälä  *                                        quantization range information
7141a2ce26f8SVille Syrjälä  * @frame: HDMI AVI infoframe
714213d0add3SVille Syrjälä  * @connector: the connector
7143779c4c28SVille Syrjälä  * @mode: DRM display mode
7144a2ce26f8SVille Syrjälä  * @rgb_quant_range: RGB quantization range (Q)
7145a2ce26f8SVille Syrjälä  */
7146a2ce26f8SVille Syrjälä void
drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe * frame,const struct drm_connector * connector,const struct drm_display_mode * mode,enum hdmi_quantization_range rgb_quant_range)7147a2ce26f8SVille Syrjälä drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
7148192a3aa0SLaurent Pinchart 				   const struct drm_connector *connector,
7149779c4c28SVille Syrjälä 				   const struct drm_display_mode *mode,
71501581b2dfSVille Syrjälä 				   enum hdmi_quantization_range rgb_quant_range)
7151a2ce26f8SVille Syrjälä {
71521581b2dfSVille Syrjälä 	const struct drm_display_info *info = &connector->display_info;
71531581b2dfSVille Syrjälä 
7154a2ce26f8SVille Syrjälä 	/*
7155a2ce26f8SVille Syrjälä 	 * CEA-861:
7156a2ce26f8SVille Syrjälä 	 * "A Source shall not send a non-zero Q value that does not correspond
7157a2ce26f8SVille Syrjälä 	 *  to the default RGB Quantization Range for the transmitted Picture
7158a2ce26f8SVille Syrjälä 	 *  unless the Sink indicates support for the Q bit in a Video
7159a2ce26f8SVille Syrjälä 	 *  Capabilities Data Block."
7160779c4c28SVille Syrjälä 	 *
7161779c4c28SVille Syrjälä 	 * HDMI 2.0 recommends sending non-zero Q when it does match the
7162779c4c28SVille Syrjälä 	 * default RGB quantization range for the mode, even when QS=0.
7163a2ce26f8SVille Syrjälä 	 */
71641581b2dfSVille Syrjälä 	if (info->rgb_quant_range_selectable ||
7165779c4c28SVille Syrjälä 	    rgb_quant_range == drm_default_rgb_quant_range(mode))
7166a2ce26f8SVille Syrjälä 		frame->quantization_range = rgb_quant_range;
7167a2ce26f8SVille Syrjälä 	else
7168a2ce26f8SVille Syrjälä 		frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
7169fcc8a22cSVille Syrjälä 
7170fcc8a22cSVille Syrjälä 	/*
7171fcc8a22cSVille Syrjälä 	 * CEA-861-F:
7172fcc8a22cSVille Syrjälä 	 * "When transmitting any RGB colorimetry, the Source should set the
7173fcc8a22cSVille Syrjälä 	 *  YQ-field to match the RGB Quantization Range being transmitted
7174fcc8a22cSVille Syrjälä 	 *  (e.g., when Limited Range RGB, set YQ=0 or when Full Range RGB,
7175fcc8a22cSVille Syrjälä 	 *  set YQ=1) and the Sink shall ignore the YQ-field."
71769271c0caSVille Syrjälä 	 *
71779271c0caSVille Syrjälä 	 * Unfortunate certain sinks (eg. VIZ Model 67/E261VA) get confused
71789271c0caSVille Syrjälä 	 * by non-zero YQ when receiving RGB. There doesn't seem to be any
71799271c0caSVille Syrjälä 	 * good way to tell which version of CEA-861 the sink supports, so
71809271c0caSVille Syrjälä 	 * we limit non-zero YQ to HDMI 2.0 sinks only as HDMI 2.0 is based
718196c92551SJilin Yuan 	 * on CEA-861-F.
7182fcc8a22cSVille Syrjälä 	 */
718313d0add3SVille Syrjälä 	if (!is_hdmi2_sink(connector) ||
71849271c0caSVille Syrjälä 	    rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED)
7185fcc8a22cSVille Syrjälä 		frame->ycc_quantization_range =
7186fcc8a22cSVille Syrjälä 			HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
7187fcc8a22cSVille Syrjälä 	else
7188fcc8a22cSVille Syrjälä 		frame->ycc_quantization_range =
7189fcc8a22cSVille Syrjälä 			HDMI_YCC_QUANTIZATION_RANGE_FULL;
7190a2ce26f8SVille Syrjälä }
7191a2ce26f8SVille Syrjälä EXPORT_SYMBOL(drm_hdmi_avi_infoframe_quant_range);
7192a2ce26f8SVille Syrjälä 
71934eed4a0aSDamien Lespiau static enum hdmi_3d_structure
s3d_structure_from_display_mode(const struct drm_display_mode * mode)71944eed4a0aSDamien Lespiau s3d_structure_from_display_mode(const struct drm_display_mode *mode)
71954eed4a0aSDamien Lespiau {
71964eed4a0aSDamien Lespiau 	u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK;
71974eed4a0aSDamien Lespiau 
71984eed4a0aSDamien Lespiau 	switch (layout) {
71994eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_FRAME_PACKING:
72004eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_FRAME_PACKING;
72014eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
72024eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE;
72034eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
72044eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE;
72054eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
72064eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL;
72074eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_L_DEPTH:
72084eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_L_DEPTH;
72094eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
72104eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH;
72114eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
72124eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM;
72134eed4a0aSDamien Lespiau 	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
72144eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF;
72154eed4a0aSDamien Lespiau 	default:
72164eed4a0aSDamien Lespiau 		return HDMI_3D_STRUCTURE_INVALID;
72174eed4a0aSDamien Lespiau 	}
72184eed4a0aSDamien Lespiau }
72194eed4a0aSDamien Lespiau 
722083dd0008SLespiau, Damien /**
722183dd0008SLespiau, Damien  * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
722283dd0008SLespiau, Damien  * data from a DRM display mode
722383dd0008SLespiau, Damien  * @frame: HDMI vendor infoframe
7224f1781e9bSVille Syrjälä  * @connector: the connector
722583dd0008SLespiau, Damien  * @mode: DRM display mode
722683dd0008SLespiau, Damien  *
722783dd0008SLespiau, Damien  * Note that there's is a need to send HDMI vendor infoframes only when using a
722883dd0008SLespiau, Damien  * 4k or stereoscopic 3D mode. So when giving any other mode as input this
722983dd0008SLespiau, Damien  * function will return -EINVAL, error that can be safely ignored.
723083dd0008SLespiau, Damien  *
7231db6cf833SThierry Reding  * Return: 0 on success or a negative error code on failure.
723283dd0008SLespiau, Damien  */
723383dd0008SLespiau, Damien int
drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe * frame,const struct drm_connector * connector,const struct drm_display_mode * mode)723483dd0008SLespiau, Damien drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
7235192a3aa0SLaurent Pinchart 					    const struct drm_connector *connector,
723683dd0008SLespiau, Damien 					    const struct drm_display_mode *mode)
723783dd0008SLespiau, Damien {
7238f1781e9bSVille Syrjälä 	/*
7239f1781e9bSVille Syrjälä 	 * FIXME: sil-sii8620 doesn't have a connector around when
7240f1781e9bSVille Syrjälä 	 * we need one, so we have to be prepared for a NULL connector.
7241f1781e9bSVille Syrjälä 	 */
7242f1781e9bSVille Syrjälä 	bool has_hdmi_infoframe = connector ?
7243f1781e9bSVille Syrjälä 		connector->display_info.has_hdmi_infoframe : false;
724483dd0008SLespiau, Damien 	int err;
724583dd0008SLespiau, Damien 
724683dd0008SLespiau, Damien 	if (!frame || !mode)
724783dd0008SLespiau, Damien 		return -EINVAL;
724883dd0008SLespiau, Damien 
7249f1781e9bSVille Syrjälä 	if (!has_hdmi_infoframe)
7250f1781e9bSVille Syrjälä 		return -EINVAL;
7251f1781e9bSVille Syrjälä 
7252949561ebSVille Syrjälä 	err = hdmi_vendor_infoframe_init(frame);
7253949561ebSVille Syrjälä 	if (err < 0)
7254949561ebSVille Syrjälä 		return err;
72554eed4a0aSDamien Lespiau 
7256f1781e9bSVille Syrjälä 	/*
7257f1781e9bSVille Syrjälä 	 * Even if it's not absolutely necessary to send the infoframe
7258f1781e9bSVille Syrjälä 	 * (ie.vic==0 and s3d_struct==0) we will still send it if we
7259f1781e9bSVille Syrjälä 	 * know that the sink can handle it. This is based on a
7260f1781e9bSVille Syrjälä 	 * suggestion in HDMI 2.0 Appendix F. Apparently some sinks
72610ae865efSCai Huoqing 	 * have trouble realizing that they should switch from 3D to 2D
7262f1781e9bSVille Syrjälä 	 * mode if the source simply stops sending the infoframe when
7263f1781e9bSVille Syrjälä 	 * it wants to switch from 3D to 2D.
7264f1781e9bSVille Syrjälä 	 */
7265949561ebSVille Syrjälä 	frame->vic = drm_mode_hdmi_vic(connector, mode);
72664eed4a0aSDamien Lespiau 	frame->s3d_struct = s3d_structure_from_display_mode(mode);
726783dd0008SLespiau, Damien 
726883dd0008SLespiau, Damien 	return 0;
726983dd0008SLespiau, Damien }
727083dd0008SLespiau, Damien EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
727140d9b043SDave Airlie 
drm_parse_tiled_block(struct drm_connector * connector,const struct displayid_block * block)72727f261afdSVille Syrjälä static void drm_parse_tiled_block(struct drm_connector *connector,
7273092c367aSVille Syrjälä 				  const struct displayid_block *block)
727440d9b043SDave Airlie {
7275092c367aSVille Syrjälä 	const struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;
727640d9b043SDave Airlie 	u16 w, h;
727740d9b043SDave Airlie 	u8 tile_v_loc, tile_h_loc;
727840d9b043SDave Airlie 	u8 num_v_tile, num_h_tile;
727940d9b043SDave Airlie 	struct drm_tile_group *tg;
728040d9b043SDave Airlie 
728140d9b043SDave Airlie 	w = tile->tile_size[0] | tile->tile_size[1] << 8;
728240d9b043SDave Airlie 	h = tile->tile_size[2] | tile->tile_size[3] << 8;
728340d9b043SDave Airlie 
728440d9b043SDave Airlie 	num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
728540d9b043SDave Airlie 	num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
728640d9b043SDave Airlie 	tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4);
728740d9b043SDave Airlie 	tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4);
728840d9b043SDave Airlie 
728940d9b043SDave Airlie 	connector->has_tile = true;
729040d9b043SDave Airlie 	if (tile->tile_cap & 0x80)
729140d9b043SDave Airlie 		connector->tile_is_single_monitor = true;
729240d9b043SDave Airlie 
729340d9b043SDave Airlie 	connector->num_h_tile = num_h_tile + 1;
729440d9b043SDave Airlie 	connector->num_v_tile = num_v_tile + 1;
729540d9b043SDave Airlie 	connector->tile_h_loc = tile_h_loc;
729640d9b043SDave Airlie 	connector->tile_v_loc = tile_v_loc;
729740d9b043SDave Airlie 	connector->tile_h_size = w + 1;
729840d9b043SDave Airlie 	connector->tile_v_size = h + 1;
729940d9b043SDave Airlie 
7300e1e7bc48SJani Nikula 	drm_dbg_kms(connector->dev,
7301e1e7bc48SJani Nikula 		    "[CONNECTOR:%d:%s] tile cap 0x%x, size %dx%d, num tiles %dx%d, location %dx%d, vend %c%c%c",
7302e1e7bc48SJani Nikula 		    connector->base.id, connector->name,
7303e1e7bc48SJani Nikula 		    tile->tile_cap,
7304e1e7bc48SJani Nikula 		    connector->tile_h_size, connector->tile_v_size,
7305e1e7bc48SJani Nikula 		    connector->num_h_tile, connector->num_v_tile,
7306e1e7bc48SJani Nikula 		    connector->tile_h_loc, connector->tile_v_loc,
7307e1e7bc48SJani Nikula 		    tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);
730840d9b043SDave Airlie 
730940d9b043SDave Airlie 	tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
731040d9b043SDave Airlie 	if (!tg)
731140d9b043SDave Airlie 		tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
731240d9b043SDave Airlie 	if (!tg)
73137f261afdSVille Syrjälä 		return;
731440d9b043SDave Airlie 
731540d9b043SDave Airlie 	if (connector->tile_group != tg) {
731640d9b043SDave Airlie 		/* if we haven't got a pointer,
731740d9b043SDave Airlie 		   take the reference, drop ref to old tile group */
7318392f9fcbSVille Syrjälä 		if (connector->tile_group)
731940d9b043SDave Airlie 			drm_mode_put_tile_group(connector->dev, connector->tile_group);
732040d9b043SDave Airlie 		connector->tile_group = tg;
7321392f9fcbSVille Syrjälä 	} else {
732240d9b043SDave Airlie 		/* if same tile group, then release the ref we just took. */
732340d9b043SDave Airlie 		drm_mode_put_tile_group(connector->dev, tg);
732440d9b043SDave Airlie 	}
732540d9b043SDave Airlie }
73265e546cd5SDave Airlie 
displayid_is_tiled_block(const struct displayid_iter * iter,const struct displayid_block * block)7327c5a486afSJani Nikula static bool displayid_is_tiled_block(const struct displayid_iter *iter,
7328c5a486afSJani Nikula 				     const struct displayid_block *block)
7329c5a486afSJani Nikula {
73306040fceaSVille Syrjälä 	return (displayid_version(iter) < DISPLAY_ID_STRUCTURE_VER_20 &&
7331c5a486afSJani Nikula 		block->tag == DATA_BLOCK_TILED_DISPLAY) ||
7332c5a486afSJani Nikula 		(displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
7333c5a486afSJani Nikula 		 block->tag == DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY);
7334c5a486afSJani Nikula }
7335c5a486afSJani Nikula 
_drm_update_tile_info(struct drm_connector * connector,const struct drm_edid * drm_edid)7336c7b2dee4SJani Nikula static void _drm_update_tile_info(struct drm_connector *connector,
7337c7b2dee4SJani Nikula 				  const struct drm_edid *drm_edid)
733840d9b043SDave Airlie {
7339bfd4e192SJani Nikula 	const struct displayid_block *block;
7340bfd4e192SJani Nikula 	struct displayid_iter iter;
734136881184SVille Syrjälä 
734240d9b043SDave Airlie 	connector->has_tile = false;
73437f261afdSVille Syrjälä 
7344d9ba1b4cSJani Nikula 	displayid_iter_edid_begin(drm_edid, &iter);
7345bfd4e192SJani Nikula 	displayid_iter_for_each(block, &iter) {
7346c5a486afSJani Nikula 		if (displayid_is_tiled_block(&iter, block))
7347bfd4e192SJani Nikula 			drm_parse_tiled_block(connector, block);
734840d9b043SDave Airlie 	}
7349bfd4e192SJani Nikula 	displayid_iter_end(&iter);
735040d9b043SDave Airlie 
73517f261afdSVille Syrjälä 	if (!connector->has_tile && connector->tile_group) {
735240d9b043SDave Airlie 		drm_mode_put_tile_group(connector->dev, connector->tile_group);
735340d9b043SDave Airlie 		connector->tile_group = NULL;
735440d9b043SDave Airlie 	}
735540d9b043SDave Airlie }
7356