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