162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2006 Luc Verhaegen (quirks list)
362306a36Sopenharmony_ci * Copyright (c) 2007-2008 Intel Corporation
462306a36Sopenharmony_ci *   Jesse Barnes <jesse.barnes@intel.com>
562306a36Sopenharmony_ci * Copyright 2010 Red Hat, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
862306a36Sopenharmony_ci * FB layer.
962306a36Sopenharmony_ci *   Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
1262306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
1362306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
1462306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sub license,
1562306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1662306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
1962306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
2062306a36Sopenharmony_ci * of the Software.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2362306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2462306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2562306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2662306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2762306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2862306a36Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/bitfield.h>
3262306a36Sopenharmony_ci#include <linux/hdmi.h>
3362306a36Sopenharmony_ci#include <linux/i2c.h>
3462306a36Sopenharmony_ci#include <linux/kernel.h>
3562306a36Sopenharmony_ci#include <linux/module.h>
3662306a36Sopenharmony_ci#include <linux/pci.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci#include <linux/vga_switcheroo.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <drm/drm_displayid.h>
4162306a36Sopenharmony_ci#include <drm/drm_drv.h>
4262306a36Sopenharmony_ci#include <drm/drm_edid.h>
4362306a36Sopenharmony_ci#include <drm/drm_encoder.h>
4462306a36Sopenharmony_ci#include <drm/drm_print.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include "drm_crtc_internal.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int oui(u8 first, u8 second, u8 third)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return (first << 16) | (second << 8) | third;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define EDID_EST_TIMINGS 16
5462306a36Sopenharmony_ci#define EDID_STD_TIMINGS 8
5562306a36Sopenharmony_ci#define EDID_DETAILED_TIMINGS 4
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * EDID blocks out in the wild have a variety of bugs, try to collect
5962306a36Sopenharmony_ci * them here (note that userspace may work around broken monitors first,
6062306a36Sopenharmony_ci * but fixes should make their way here so that the kernel "just works"
6162306a36Sopenharmony_ci * on as many displays as possible).
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* First detailed mode wrong, use largest 60Hz mode */
6562306a36Sopenharmony_ci#define EDID_QUIRK_PREFER_LARGE_60		(1 << 0)
6662306a36Sopenharmony_ci/* Reported 135MHz pixel clock is too high, needs adjustment */
6762306a36Sopenharmony_ci#define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1)
6862306a36Sopenharmony_ci/* Prefer the largest mode at 75 Hz */
6962306a36Sopenharmony_ci#define EDID_QUIRK_PREFER_LARGE_75		(1 << 2)
7062306a36Sopenharmony_ci/* Detail timing is in cm not mm */
7162306a36Sopenharmony_ci#define EDID_QUIRK_DETAILED_IN_CM		(1 << 3)
7262306a36Sopenharmony_ci/* Detailed timing descriptors have bogus size values, so just take the
7362306a36Sopenharmony_ci * maximum size and use that.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4)
7662306a36Sopenharmony_ci/* use +hsync +vsync for detailed mode */
7762306a36Sopenharmony_ci#define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
7862306a36Sopenharmony_ci/* Force reduced-blanking timings for detailed modes */
7962306a36Sopenharmony_ci#define EDID_QUIRK_FORCE_REDUCED_BLANKING	(1 << 7)
8062306a36Sopenharmony_ci/* Force 8bpc */
8162306a36Sopenharmony_ci#define EDID_QUIRK_FORCE_8BPC			(1 << 8)
8262306a36Sopenharmony_ci/* Force 12bpc */
8362306a36Sopenharmony_ci#define EDID_QUIRK_FORCE_12BPC			(1 << 9)
8462306a36Sopenharmony_ci/* Force 6bpc */
8562306a36Sopenharmony_ci#define EDID_QUIRK_FORCE_6BPC			(1 << 10)
8662306a36Sopenharmony_ci/* Force 10bpc */
8762306a36Sopenharmony_ci#define EDID_QUIRK_FORCE_10BPC			(1 << 11)
8862306a36Sopenharmony_ci/* Non desktop display (i.e. HMD) */
8962306a36Sopenharmony_ci#define EDID_QUIRK_NON_DESKTOP			(1 << 12)
9062306a36Sopenharmony_ci/* Cap the DSC target bitrate to 15bpp */
9162306a36Sopenharmony_ci#define EDID_QUIRK_CAP_DSC_15BPP		(1 << 13)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define MICROSOFT_IEEE_OUI	0xca125c
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct detailed_mode_closure {
9662306a36Sopenharmony_ci	struct drm_connector *connector;
9762306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
9862306a36Sopenharmony_ci	bool preferred;
9962306a36Sopenharmony_ci	int modes;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define LEVEL_DMT	0
10362306a36Sopenharmony_ci#define LEVEL_GTF	1
10462306a36Sopenharmony_ci#define LEVEL_GTF2	2
10562306a36Sopenharmony_ci#define LEVEL_CVT	3
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define EDID_QUIRK(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _quirks) \
10862306a36Sopenharmony_ci{ \
10962306a36Sopenharmony_ci	.panel_id = drm_edid_encode_panel_id(vend_chr_0, vend_chr_1, vend_chr_2, \
11062306a36Sopenharmony_ci					     product_id), \
11162306a36Sopenharmony_ci	.quirks = _quirks \
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct edid_quirk {
11562306a36Sopenharmony_ci	u32 panel_id;
11662306a36Sopenharmony_ci	u32 quirks;
11762306a36Sopenharmony_ci} edid_quirk_list[] = {
11862306a36Sopenharmony_ci	/* Acer AL1706 */
11962306a36Sopenharmony_ci	EDID_QUIRK('A', 'C', 'R', 44358, EDID_QUIRK_PREFER_LARGE_60),
12062306a36Sopenharmony_ci	/* Acer F51 */
12162306a36Sopenharmony_ci	EDID_QUIRK('A', 'P', 'I', 0x7602, EDID_QUIRK_PREFER_LARGE_60),
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
12462306a36Sopenharmony_ci	EDID_QUIRK('A', 'E', 'O', 0, EDID_QUIRK_FORCE_6BPC),
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* BenQ GW2765 */
12762306a36Sopenharmony_ci	EDID_QUIRK('B', 'N', 'Q', 0x78d6, EDID_QUIRK_FORCE_8BPC),
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* BOE model on HP Pavilion 15-n233sl reports 8 bpc, but is a 6 bpc panel */
13062306a36Sopenharmony_ci	EDID_QUIRK('B', 'O', 'E', 0x78b, EDID_QUIRK_FORCE_6BPC),
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */
13362306a36Sopenharmony_ci	EDID_QUIRK('C', 'P', 'T', 0x17df, EDID_QUIRK_FORCE_6BPC),
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* SDC panel of Lenovo B50-80 reports 8 bpc, but is a 6 bpc panel */
13662306a36Sopenharmony_ci	EDID_QUIRK('S', 'D', 'C', 0x3652, EDID_QUIRK_FORCE_6BPC),
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* BOE model 0x0771 reports 8 bpc, but is a 6 bpc panel */
13962306a36Sopenharmony_ci	EDID_QUIRK('B', 'O', 'E', 0x0771, EDID_QUIRK_FORCE_6BPC),
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Belinea 10 15 55 */
14262306a36Sopenharmony_ci	EDID_QUIRK('M', 'A', 'X', 1516, EDID_QUIRK_PREFER_LARGE_60),
14362306a36Sopenharmony_ci	EDID_QUIRK('M', 'A', 'X', 0x77e, EDID_QUIRK_PREFER_LARGE_60),
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Envision Peripherals, Inc. EN-7100e */
14662306a36Sopenharmony_ci	EDID_QUIRK('E', 'P', 'I', 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH),
14762306a36Sopenharmony_ci	/* Envision EN2028 */
14862306a36Sopenharmony_ci	EDID_QUIRK('E', 'P', 'I', 8232, EDID_QUIRK_PREFER_LARGE_60),
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Funai Electronics PM36B */
15162306a36Sopenharmony_ci	EDID_QUIRK('F', 'C', 'M', 13600, EDID_QUIRK_PREFER_LARGE_75 |
15262306a36Sopenharmony_ci				       EDID_QUIRK_DETAILED_IN_CM),
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* LG 27GP950 */
15562306a36Sopenharmony_ci	EDID_QUIRK('G', 'S', 'M', 0x5bbf, EDID_QUIRK_CAP_DSC_15BPP),
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* LG 27GN950 */
15862306a36Sopenharmony_ci	EDID_QUIRK('G', 'S', 'M', 0x5b9a, EDID_QUIRK_CAP_DSC_15BPP),
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */
16162306a36Sopenharmony_ci	EDID_QUIRK('L', 'G', 'D', 764, EDID_QUIRK_FORCE_10BPC),
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* LG Philips LCD LP154W01-A5 */
16462306a36Sopenharmony_ci	EDID_QUIRK('L', 'P', 'L', 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE),
16562306a36Sopenharmony_ci	EDID_QUIRK('L', 'P', 'L', 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE),
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Samsung SyncMaster 205BW.  Note: irony */
16862306a36Sopenharmony_ci	EDID_QUIRK('S', 'A', 'M', 541, EDID_QUIRK_DETAILED_SYNC_PP),
16962306a36Sopenharmony_ci	/* Samsung SyncMaster 22[5-6]BW */
17062306a36Sopenharmony_ci	EDID_QUIRK('S', 'A', 'M', 596, EDID_QUIRK_PREFER_LARGE_60),
17162306a36Sopenharmony_ci	EDID_QUIRK('S', 'A', 'M', 638, EDID_QUIRK_PREFER_LARGE_60),
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
17462306a36Sopenharmony_ci	EDID_QUIRK('S', 'N', 'Y', 0x2541, EDID_QUIRK_FORCE_12BPC),
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* ViewSonic VA2026w */
17762306a36Sopenharmony_ci	EDID_QUIRK('V', 'S', 'C', 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING),
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Medion MD 30217 PG */
18062306a36Sopenharmony_ci	EDID_QUIRK('M', 'E', 'D', 0x7b8, EDID_QUIRK_PREFER_LARGE_75),
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Lenovo G50 */
18362306a36Sopenharmony_ci	EDID_QUIRK('S', 'D', 'C', 18514, EDID_QUIRK_FORCE_6BPC),
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
18662306a36Sopenharmony_ci	EDID_QUIRK('S', 'E', 'C', 0xd033, EDID_QUIRK_FORCE_8BPC),
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
18962306a36Sopenharmony_ci	EDID_QUIRK('E', 'T', 'R', 13896, EDID_QUIRK_FORCE_8BPC),
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Valve Index Headset */
19262306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91a8, EDID_QUIRK_NON_DESKTOP),
19362306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b0, EDID_QUIRK_NON_DESKTOP),
19462306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b1, EDID_QUIRK_NON_DESKTOP),
19562306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b2, EDID_QUIRK_NON_DESKTOP),
19662306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b3, EDID_QUIRK_NON_DESKTOP),
19762306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b4, EDID_QUIRK_NON_DESKTOP),
19862306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b5, EDID_QUIRK_NON_DESKTOP),
19962306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b6, EDID_QUIRK_NON_DESKTOP),
20062306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b7, EDID_QUIRK_NON_DESKTOP),
20162306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b8, EDID_QUIRK_NON_DESKTOP),
20262306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91b9, EDID_QUIRK_NON_DESKTOP),
20362306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91ba, EDID_QUIRK_NON_DESKTOP),
20462306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91bb, EDID_QUIRK_NON_DESKTOP),
20562306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91bc, EDID_QUIRK_NON_DESKTOP),
20662306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91bd, EDID_QUIRK_NON_DESKTOP),
20762306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91be, EDID_QUIRK_NON_DESKTOP),
20862306a36Sopenharmony_ci	EDID_QUIRK('V', 'L', 'V', 0x91bf, EDID_QUIRK_NON_DESKTOP),
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* HTC Vive and Vive Pro VR Headsets */
21162306a36Sopenharmony_ci	EDID_QUIRK('H', 'V', 'R', 0xaa01, EDID_QUIRK_NON_DESKTOP),
21262306a36Sopenharmony_ci	EDID_QUIRK('H', 'V', 'R', 0xaa02, EDID_QUIRK_NON_DESKTOP),
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Oculus Rift DK1, DK2, CV1 and Rift S VR Headsets */
21562306a36Sopenharmony_ci	EDID_QUIRK('O', 'V', 'R', 0x0001, EDID_QUIRK_NON_DESKTOP),
21662306a36Sopenharmony_ci	EDID_QUIRK('O', 'V', 'R', 0x0003, EDID_QUIRK_NON_DESKTOP),
21762306a36Sopenharmony_ci	EDID_QUIRK('O', 'V', 'R', 0x0004, EDID_QUIRK_NON_DESKTOP),
21862306a36Sopenharmony_ci	EDID_QUIRK('O', 'V', 'R', 0x0012, EDID_QUIRK_NON_DESKTOP),
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Windows Mixed Reality Headsets */
22162306a36Sopenharmony_ci	EDID_QUIRK('A', 'C', 'R', 0x7fce, EDID_QUIRK_NON_DESKTOP),
22262306a36Sopenharmony_ci	EDID_QUIRK('L', 'E', 'N', 0x0408, EDID_QUIRK_NON_DESKTOP),
22362306a36Sopenharmony_ci	EDID_QUIRK('F', 'U', 'J', 0x1970, EDID_QUIRK_NON_DESKTOP),
22462306a36Sopenharmony_ci	EDID_QUIRK('D', 'E', 'L', 0x7fce, EDID_QUIRK_NON_DESKTOP),
22562306a36Sopenharmony_ci	EDID_QUIRK('S', 'E', 'C', 0x144a, EDID_QUIRK_NON_DESKTOP),
22662306a36Sopenharmony_ci	EDID_QUIRK('A', 'U', 'S', 0xc102, EDID_QUIRK_NON_DESKTOP),
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Sony PlayStation VR Headset */
22962306a36Sopenharmony_ci	EDID_QUIRK('S', 'N', 'Y', 0x0704, EDID_QUIRK_NON_DESKTOP),
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Sensics VR Headsets */
23262306a36Sopenharmony_ci	EDID_QUIRK('S', 'E', 'N', 0x1019, EDID_QUIRK_NON_DESKTOP),
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* OSVR HDK and HDK2 VR Headsets */
23562306a36Sopenharmony_ci	EDID_QUIRK('S', 'V', 'R', 0x1019, EDID_QUIRK_NON_DESKTOP),
23662306a36Sopenharmony_ci	EDID_QUIRK('A', 'U', 'O', 0x1111, EDID_QUIRK_NON_DESKTOP),
23762306a36Sopenharmony_ci};
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * Autogenerated from the DMT spec.
24162306a36Sopenharmony_ci * This table is copied from xfree86/modes/xf86EdidModes.c.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistatic const struct drm_display_mode drm_dmt_modes[] = {
24462306a36Sopenharmony_ci	/* 0x01 - 640x350@85Hz */
24562306a36Sopenharmony_ci	{ DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
24662306a36Sopenharmony_ci		   736, 832, 0, 350, 382, 385, 445, 0,
24762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
24862306a36Sopenharmony_ci	/* 0x02 - 640x400@85Hz */
24962306a36Sopenharmony_ci	{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
25062306a36Sopenharmony_ci		   736, 832, 0, 400, 401, 404, 445, 0,
25162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
25262306a36Sopenharmony_ci	/* 0x03 - 720x400@85Hz */
25362306a36Sopenharmony_ci	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
25462306a36Sopenharmony_ci		   828, 936, 0, 400, 401, 404, 446, 0,
25562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
25662306a36Sopenharmony_ci	/* 0x04 - 640x480@60Hz */
25762306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
25862306a36Sopenharmony_ci		   752, 800, 0, 480, 490, 492, 525, 0,
25962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26062306a36Sopenharmony_ci	/* 0x05 - 640x480@72Hz */
26162306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
26262306a36Sopenharmony_ci		   704, 832, 0, 480, 489, 492, 520, 0,
26362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26462306a36Sopenharmony_ci	/* 0x06 - 640x480@75Hz */
26562306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
26662306a36Sopenharmony_ci		   720, 840, 0, 480, 481, 484, 500, 0,
26762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
26862306a36Sopenharmony_ci	/* 0x07 - 640x480@85Hz */
26962306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
27062306a36Sopenharmony_ci		   752, 832, 0, 480, 481, 484, 509, 0,
27162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
27262306a36Sopenharmony_ci	/* 0x08 - 800x600@56Hz */
27362306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
27462306a36Sopenharmony_ci		   896, 1024, 0, 600, 601, 603, 625, 0,
27562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
27662306a36Sopenharmony_ci	/* 0x09 - 800x600@60Hz */
27762306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
27862306a36Sopenharmony_ci		   968, 1056, 0, 600, 601, 605, 628, 0,
27962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28062306a36Sopenharmony_ci	/* 0x0a - 800x600@72Hz */
28162306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
28262306a36Sopenharmony_ci		   976, 1040, 0, 600, 637, 643, 666, 0,
28362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28462306a36Sopenharmony_ci	/* 0x0b - 800x600@75Hz */
28562306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
28662306a36Sopenharmony_ci		   896, 1056, 0, 600, 601, 604, 625, 0,
28762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
28862306a36Sopenharmony_ci	/* 0x0c - 800x600@85Hz */
28962306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
29062306a36Sopenharmony_ci		   896, 1048, 0, 600, 601, 604, 631, 0,
29162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
29262306a36Sopenharmony_ci	/* 0x0d - 800x600@120Hz RB */
29362306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 73250, 800, 848,
29462306a36Sopenharmony_ci		   880, 960, 0, 600, 603, 607, 636, 0,
29562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
29662306a36Sopenharmony_ci	/* 0x0e - 848x480@60Hz */
29762306a36Sopenharmony_ci	{ DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
29862306a36Sopenharmony_ci		   976, 1088, 0, 480, 486, 494, 517, 0,
29962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
30062306a36Sopenharmony_ci	/* 0x0f - 1024x768@43Hz, interlace */
30162306a36Sopenharmony_ci	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
30262306a36Sopenharmony_ci		   1208, 1264, 0, 768, 768, 776, 817, 0,
30362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
30462306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE) },
30562306a36Sopenharmony_ci	/* 0x10 - 1024x768@60Hz */
30662306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
30762306a36Sopenharmony_ci		   1184, 1344, 0, 768, 771, 777, 806, 0,
30862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
30962306a36Sopenharmony_ci	/* 0x11 - 1024x768@70Hz */
31062306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
31162306a36Sopenharmony_ci		   1184, 1328, 0, 768, 771, 777, 806, 0,
31262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
31362306a36Sopenharmony_ci	/* 0x12 - 1024x768@75Hz */
31462306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
31562306a36Sopenharmony_ci		   1136, 1312, 0, 768, 769, 772, 800, 0,
31662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
31762306a36Sopenharmony_ci	/* 0x13 - 1024x768@85Hz */
31862306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
31962306a36Sopenharmony_ci		   1168, 1376, 0, 768, 769, 772, 808, 0,
32062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
32162306a36Sopenharmony_ci	/* 0x14 - 1024x768@120Hz RB */
32262306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 115500, 1024, 1072,
32362306a36Sopenharmony_ci		   1104, 1184, 0, 768, 771, 775, 813, 0,
32462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
32562306a36Sopenharmony_ci	/* 0x15 - 1152x864@75Hz */
32662306a36Sopenharmony_ci	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
32762306a36Sopenharmony_ci		   1344, 1600, 0, 864, 865, 868, 900, 0,
32862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
32962306a36Sopenharmony_ci	/* 0x55 - 1280x720@60Hz */
33062306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
33162306a36Sopenharmony_ci		   1430, 1650, 0, 720, 725, 730, 750, 0,
33262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
33362306a36Sopenharmony_ci	/* 0x16 - 1280x768@60Hz RB */
33462306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 68250, 1280, 1328,
33562306a36Sopenharmony_ci		   1360, 1440, 0, 768, 771, 778, 790, 0,
33662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
33762306a36Sopenharmony_ci	/* 0x17 - 1280x768@60Hz */
33862306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
33962306a36Sopenharmony_ci		   1472, 1664, 0, 768, 771, 778, 798, 0,
34062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34162306a36Sopenharmony_ci	/* 0x18 - 1280x768@75Hz */
34262306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
34362306a36Sopenharmony_ci		   1488, 1696, 0, 768, 771, 778, 805, 0,
34462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34562306a36Sopenharmony_ci	/* 0x19 - 1280x768@85Hz */
34662306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
34762306a36Sopenharmony_ci		   1496, 1712, 0, 768, 771, 778, 809, 0,
34862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
34962306a36Sopenharmony_ci	/* 0x1a - 1280x768@120Hz RB */
35062306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 140250, 1280, 1328,
35162306a36Sopenharmony_ci		   1360, 1440, 0, 768, 771, 778, 813, 0,
35262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
35362306a36Sopenharmony_ci	/* 0x1b - 1280x800@60Hz RB */
35462306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 71000, 1280, 1328,
35562306a36Sopenharmony_ci		   1360, 1440, 0, 800, 803, 809, 823, 0,
35662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
35762306a36Sopenharmony_ci	/* 0x1c - 1280x800@60Hz */
35862306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
35962306a36Sopenharmony_ci		   1480, 1680, 0, 800, 803, 809, 831, 0,
36062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36162306a36Sopenharmony_ci	/* 0x1d - 1280x800@75Hz */
36262306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
36362306a36Sopenharmony_ci		   1488, 1696, 0, 800, 803, 809, 838, 0,
36462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36562306a36Sopenharmony_ci	/* 0x1e - 1280x800@85Hz */
36662306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
36762306a36Sopenharmony_ci		   1496, 1712, 0, 800, 803, 809, 843, 0,
36862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
36962306a36Sopenharmony_ci	/* 0x1f - 1280x800@120Hz RB */
37062306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 146250, 1280, 1328,
37162306a36Sopenharmony_ci		   1360, 1440, 0, 800, 803, 809, 847, 0,
37262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
37362306a36Sopenharmony_ci	/* 0x20 - 1280x960@60Hz */
37462306a36Sopenharmony_ci	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
37562306a36Sopenharmony_ci		   1488, 1800, 0, 960, 961, 964, 1000, 0,
37662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
37762306a36Sopenharmony_ci	/* 0x21 - 1280x960@85Hz */
37862306a36Sopenharmony_ci	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
37962306a36Sopenharmony_ci		   1504, 1728, 0, 960, 961, 964, 1011, 0,
38062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
38162306a36Sopenharmony_ci	/* 0x22 - 1280x960@120Hz RB */
38262306a36Sopenharmony_ci	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 175500, 1280, 1328,
38362306a36Sopenharmony_ci		   1360, 1440, 0, 960, 963, 967, 1017, 0,
38462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
38562306a36Sopenharmony_ci	/* 0x23 - 1280x1024@60Hz */
38662306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
38762306a36Sopenharmony_ci		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
38862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
38962306a36Sopenharmony_ci	/* 0x24 - 1280x1024@75Hz */
39062306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
39162306a36Sopenharmony_ci		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
39262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
39362306a36Sopenharmony_ci	/* 0x25 - 1280x1024@85Hz */
39462306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
39562306a36Sopenharmony_ci		   1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
39662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
39762306a36Sopenharmony_ci	/* 0x26 - 1280x1024@120Hz RB */
39862306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 187250, 1280, 1328,
39962306a36Sopenharmony_ci		   1360, 1440, 0, 1024, 1027, 1034, 1084, 0,
40062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
40162306a36Sopenharmony_ci	/* 0x27 - 1360x768@60Hz */
40262306a36Sopenharmony_ci	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
40362306a36Sopenharmony_ci		   1536, 1792, 0, 768, 771, 777, 795, 0,
40462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
40562306a36Sopenharmony_ci	/* 0x28 - 1360x768@120Hz RB */
40662306a36Sopenharmony_ci	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 148250, 1360, 1408,
40762306a36Sopenharmony_ci		   1440, 1520, 0, 768, 771, 776, 813, 0,
40862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
40962306a36Sopenharmony_ci	/* 0x51 - 1366x768@60Hz */
41062306a36Sopenharmony_ci	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 85500, 1366, 1436,
41162306a36Sopenharmony_ci		   1579, 1792, 0, 768, 771, 774, 798, 0,
41262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
41362306a36Sopenharmony_ci	/* 0x56 - 1366x768@60Hz */
41462306a36Sopenharmony_ci	{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 72000, 1366, 1380,
41562306a36Sopenharmony_ci		   1436, 1500, 0, 768, 769, 772, 800, 0,
41662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
41762306a36Sopenharmony_ci	/* 0x29 - 1400x1050@60Hz RB */
41862306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 101000, 1400, 1448,
41962306a36Sopenharmony_ci		   1480, 1560, 0, 1050, 1053, 1057, 1080, 0,
42062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
42162306a36Sopenharmony_ci	/* 0x2a - 1400x1050@60Hz */
42262306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
42362306a36Sopenharmony_ci		   1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
42462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
42562306a36Sopenharmony_ci	/* 0x2b - 1400x1050@75Hz */
42662306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
42762306a36Sopenharmony_ci		   1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
42862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
42962306a36Sopenharmony_ci	/* 0x2c - 1400x1050@85Hz */
43062306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
43162306a36Sopenharmony_ci		   1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
43262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
43362306a36Sopenharmony_ci	/* 0x2d - 1400x1050@120Hz RB */
43462306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 208000, 1400, 1448,
43562306a36Sopenharmony_ci		   1480, 1560, 0, 1050, 1053, 1057, 1112, 0,
43662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
43762306a36Sopenharmony_ci	/* 0x2e - 1440x900@60Hz RB */
43862306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 88750, 1440, 1488,
43962306a36Sopenharmony_ci		   1520, 1600, 0, 900, 903, 909, 926, 0,
44062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
44162306a36Sopenharmony_ci	/* 0x2f - 1440x900@60Hz */
44262306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
44362306a36Sopenharmony_ci		   1672, 1904, 0, 900, 903, 909, 934, 0,
44462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44562306a36Sopenharmony_ci	/* 0x30 - 1440x900@75Hz */
44662306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
44762306a36Sopenharmony_ci		   1688, 1936, 0, 900, 903, 909, 942, 0,
44862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
44962306a36Sopenharmony_ci	/* 0x31 - 1440x900@85Hz */
45062306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
45162306a36Sopenharmony_ci		   1696, 1952, 0, 900, 903, 909, 948, 0,
45262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
45362306a36Sopenharmony_ci	/* 0x32 - 1440x900@120Hz RB */
45462306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 182750, 1440, 1488,
45562306a36Sopenharmony_ci		   1520, 1600, 0, 900, 903, 909, 953, 0,
45662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
45762306a36Sopenharmony_ci	/* 0x53 - 1600x900@60Hz */
45862306a36Sopenharmony_ci	{ DRM_MODE("1600x900", DRM_MODE_TYPE_DRIVER, 108000, 1600, 1624,
45962306a36Sopenharmony_ci		   1704, 1800, 0, 900, 901, 904, 1000, 0,
46062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46162306a36Sopenharmony_ci	/* 0x33 - 1600x1200@60Hz */
46262306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
46362306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
46462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46562306a36Sopenharmony_ci	/* 0x34 - 1600x1200@65Hz */
46662306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
46762306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
46862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
46962306a36Sopenharmony_ci	/* 0x35 - 1600x1200@70Hz */
47062306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
47162306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
47262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
47362306a36Sopenharmony_ci	/* 0x36 - 1600x1200@75Hz */
47462306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
47562306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
47662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
47762306a36Sopenharmony_ci	/* 0x37 - 1600x1200@85Hz */
47862306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
47962306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
48062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
48162306a36Sopenharmony_ci	/* 0x38 - 1600x1200@120Hz RB */
48262306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 268250, 1600, 1648,
48362306a36Sopenharmony_ci		   1680, 1760, 0, 1200, 1203, 1207, 1271, 0,
48462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
48562306a36Sopenharmony_ci	/* 0x39 - 1680x1050@60Hz RB */
48662306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 119000, 1680, 1728,
48762306a36Sopenharmony_ci		   1760, 1840, 0, 1050, 1053, 1059, 1080, 0,
48862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
48962306a36Sopenharmony_ci	/* 0x3a - 1680x1050@60Hz */
49062306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
49162306a36Sopenharmony_ci		   1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
49262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49362306a36Sopenharmony_ci	/* 0x3b - 1680x1050@75Hz */
49462306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
49562306a36Sopenharmony_ci		   1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
49662306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
49762306a36Sopenharmony_ci	/* 0x3c - 1680x1050@85Hz */
49862306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
49962306a36Sopenharmony_ci		   1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
50062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
50162306a36Sopenharmony_ci	/* 0x3d - 1680x1050@120Hz RB */
50262306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 245500, 1680, 1728,
50362306a36Sopenharmony_ci		   1760, 1840, 0, 1050, 1053, 1059, 1112, 0,
50462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
50562306a36Sopenharmony_ci	/* 0x3e - 1792x1344@60Hz */
50662306a36Sopenharmony_ci	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
50762306a36Sopenharmony_ci		   2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
50862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
50962306a36Sopenharmony_ci	/* 0x3f - 1792x1344@75Hz */
51062306a36Sopenharmony_ci	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
51162306a36Sopenharmony_ci		   2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
51262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
51362306a36Sopenharmony_ci	/* 0x40 - 1792x1344@120Hz RB */
51462306a36Sopenharmony_ci	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 333250, 1792, 1840,
51562306a36Sopenharmony_ci		   1872, 1952, 0, 1344, 1347, 1351, 1423, 0,
51662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
51762306a36Sopenharmony_ci	/* 0x41 - 1856x1392@60Hz */
51862306a36Sopenharmony_ci	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
51962306a36Sopenharmony_ci		   2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
52062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
52162306a36Sopenharmony_ci	/* 0x42 - 1856x1392@75Hz */
52262306a36Sopenharmony_ci	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
52362306a36Sopenharmony_ci		   2208, 2560, 0, 1392, 1393, 1396, 1500, 0,
52462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
52562306a36Sopenharmony_ci	/* 0x43 - 1856x1392@120Hz RB */
52662306a36Sopenharmony_ci	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 356500, 1856, 1904,
52762306a36Sopenharmony_ci		   1936, 2016, 0, 1392, 1395, 1399, 1474, 0,
52862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
52962306a36Sopenharmony_ci	/* 0x52 - 1920x1080@60Hz */
53062306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
53162306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
53262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
53362306a36Sopenharmony_ci	/* 0x44 - 1920x1200@60Hz RB */
53462306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 154000, 1920, 1968,
53562306a36Sopenharmony_ci		   2000, 2080, 0, 1200, 1203, 1209, 1235, 0,
53662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
53762306a36Sopenharmony_ci	/* 0x45 - 1920x1200@60Hz */
53862306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
53962306a36Sopenharmony_ci		   2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
54062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54162306a36Sopenharmony_ci	/* 0x46 - 1920x1200@75Hz */
54262306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
54362306a36Sopenharmony_ci		   2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
54462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54562306a36Sopenharmony_ci	/* 0x47 - 1920x1200@85Hz */
54662306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
54762306a36Sopenharmony_ci		   2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
54862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
54962306a36Sopenharmony_ci	/* 0x48 - 1920x1200@120Hz RB */
55062306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 317000, 1920, 1968,
55162306a36Sopenharmony_ci		   2000, 2080, 0, 1200, 1203, 1209, 1271, 0,
55262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
55362306a36Sopenharmony_ci	/* 0x49 - 1920x1440@60Hz */
55462306a36Sopenharmony_ci	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
55562306a36Sopenharmony_ci		   2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
55662306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
55762306a36Sopenharmony_ci	/* 0x4a - 1920x1440@75Hz */
55862306a36Sopenharmony_ci	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
55962306a36Sopenharmony_ci		   2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
56062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
56162306a36Sopenharmony_ci	/* 0x4b - 1920x1440@120Hz RB */
56262306a36Sopenharmony_ci	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 380500, 1920, 1968,
56362306a36Sopenharmony_ci		   2000, 2080, 0, 1440, 1443, 1447, 1525, 0,
56462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
56562306a36Sopenharmony_ci	/* 0x54 - 2048x1152@60Hz */
56662306a36Sopenharmony_ci	{ DRM_MODE("2048x1152", DRM_MODE_TYPE_DRIVER, 162000, 2048, 2074,
56762306a36Sopenharmony_ci		   2154, 2250, 0, 1152, 1153, 1156, 1200, 0,
56862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
56962306a36Sopenharmony_ci	/* 0x4c - 2560x1600@60Hz RB */
57062306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 268500, 2560, 2608,
57162306a36Sopenharmony_ci		   2640, 2720, 0, 1600, 1603, 1609, 1646, 0,
57262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
57362306a36Sopenharmony_ci	/* 0x4d - 2560x1600@60Hz */
57462306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
57562306a36Sopenharmony_ci		   3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
57662306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
57762306a36Sopenharmony_ci	/* 0x4e - 2560x1600@75Hz */
57862306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
57962306a36Sopenharmony_ci		   3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
58062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
58162306a36Sopenharmony_ci	/* 0x4f - 2560x1600@85Hz */
58262306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
58362306a36Sopenharmony_ci		   3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
58462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
58562306a36Sopenharmony_ci	/* 0x50 - 2560x1600@120Hz RB */
58662306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 552750, 2560, 2608,
58762306a36Sopenharmony_ci		   2640, 2720, 0, 1600, 1603, 1609, 1694, 0,
58862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
58962306a36Sopenharmony_ci	/* 0x57 - 4096x2160@60Hz RB */
59062306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556744, 4096, 4104,
59162306a36Sopenharmony_ci		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
59262306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
59362306a36Sopenharmony_ci	/* 0x58 - 4096x2160@59.94Hz RB */
59462306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104,
59562306a36Sopenharmony_ci		   4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
59662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
59762306a36Sopenharmony_ci};
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/*
60062306a36Sopenharmony_ci * These more or less come from the DMT spec.  The 720x400 modes are
60162306a36Sopenharmony_ci * inferred from historical 80x25 practice.  The 640x480@67 and 832x624@75
60262306a36Sopenharmony_ci * modes are old-school Mac modes.  The EDID spec says the 1152x864@75 mode
60362306a36Sopenharmony_ci * should be 1152x870, again for the Mac, but instead we use the x864 DMT
60462306a36Sopenharmony_ci * mode.
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * The DMT modes have been fact-checked; the rest are mild guesses.
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_cistatic const struct drm_display_mode edid_est_modes[] = {
60962306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
61062306a36Sopenharmony_ci		   968, 1056, 0, 600, 601, 605, 628, 0,
61162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
61262306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
61362306a36Sopenharmony_ci		   896, 1024, 0, 600, 601, 603,  625, 0,
61462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
61562306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
61662306a36Sopenharmony_ci		   720, 840, 0, 480, 481, 484, 500, 0,
61762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
61862306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
61962306a36Sopenharmony_ci		   704,  832, 0, 480, 489, 492, 520, 0,
62062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
62162306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
62262306a36Sopenharmony_ci		   768,  864, 0, 480, 483, 486, 525, 0,
62362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
62462306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
62562306a36Sopenharmony_ci		   752, 800, 0, 480, 490, 492, 525, 0,
62662306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
62762306a36Sopenharmony_ci	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
62862306a36Sopenharmony_ci		   846, 900, 0, 400, 421, 423,  449, 0,
62962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
63062306a36Sopenharmony_ci	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
63162306a36Sopenharmony_ci		   846,  900, 0, 400, 412, 414, 449, 0,
63262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
63362306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
63462306a36Sopenharmony_ci		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
63562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
63662306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
63762306a36Sopenharmony_ci		   1136, 1312, 0,  768, 769, 772, 800, 0,
63862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
63962306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
64062306a36Sopenharmony_ci		   1184, 1328, 0,  768, 771, 777, 806, 0,
64162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
64262306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
64362306a36Sopenharmony_ci		   1184, 1344, 0,  768, 771, 777, 806, 0,
64462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
64562306a36Sopenharmony_ci	{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
64662306a36Sopenharmony_ci		   1208, 1264, 0, 768, 768, 776, 817, 0,
64762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
64862306a36Sopenharmony_ci	{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
64962306a36Sopenharmony_ci		   928, 1152, 0, 624, 625, 628, 667, 0,
65062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
65162306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
65262306a36Sopenharmony_ci		   896, 1056, 0, 600, 601, 604,  625, 0,
65362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
65462306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
65562306a36Sopenharmony_ci		   976, 1040, 0, 600, 637, 643, 666, 0,
65662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
65762306a36Sopenharmony_ci	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
65862306a36Sopenharmony_ci		   1344, 1600, 0,  864, 865, 868, 900, 0,
65962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistruct minimode {
66362306a36Sopenharmony_ci	short w;
66462306a36Sopenharmony_ci	short h;
66562306a36Sopenharmony_ci	short r;
66662306a36Sopenharmony_ci	short rb;
66762306a36Sopenharmony_ci};
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic const struct minimode est3_modes[] = {
67062306a36Sopenharmony_ci	/* byte 6 */
67162306a36Sopenharmony_ci	{ 640, 350, 85, 0 },
67262306a36Sopenharmony_ci	{ 640, 400, 85, 0 },
67362306a36Sopenharmony_ci	{ 720, 400, 85, 0 },
67462306a36Sopenharmony_ci	{ 640, 480, 85, 0 },
67562306a36Sopenharmony_ci	{ 848, 480, 60, 0 },
67662306a36Sopenharmony_ci	{ 800, 600, 85, 0 },
67762306a36Sopenharmony_ci	{ 1024, 768, 85, 0 },
67862306a36Sopenharmony_ci	{ 1152, 864, 75, 0 },
67962306a36Sopenharmony_ci	/* byte 7 */
68062306a36Sopenharmony_ci	{ 1280, 768, 60, 1 },
68162306a36Sopenharmony_ci	{ 1280, 768, 60, 0 },
68262306a36Sopenharmony_ci	{ 1280, 768, 75, 0 },
68362306a36Sopenharmony_ci	{ 1280, 768, 85, 0 },
68462306a36Sopenharmony_ci	{ 1280, 960, 60, 0 },
68562306a36Sopenharmony_ci	{ 1280, 960, 85, 0 },
68662306a36Sopenharmony_ci	{ 1280, 1024, 60, 0 },
68762306a36Sopenharmony_ci	{ 1280, 1024, 85, 0 },
68862306a36Sopenharmony_ci	/* byte 8 */
68962306a36Sopenharmony_ci	{ 1360, 768, 60, 0 },
69062306a36Sopenharmony_ci	{ 1440, 900, 60, 1 },
69162306a36Sopenharmony_ci	{ 1440, 900, 60, 0 },
69262306a36Sopenharmony_ci	{ 1440, 900, 75, 0 },
69362306a36Sopenharmony_ci	{ 1440, 900, 85, 0 },
69462306a36Sopenharmony_ci	{ 1400, 1050, 60, 1 },
69562306a36Sopenharmony_ci	{ 1400, 1050, 60, 0 },
69662306a36Sopenharmony_ci	{ 1400, 1050, 75, 0 },
69762306a36Sopenharmony_ci	/* byte 9 */
69862306a36Sopenharmony_ci	{ 1400, 1050, 85, 0 },
69962306a36Sopenharmony_ci	{ 1680, 1050, 60, 1 },
70062306a36Sopenharmony_ci	{ 1680, 1050, 60, 0 },
70162306a36Sopenharmony_ci	{ 1680, 1050, 75, 0 },
70262306a36Sopenharmony_ci	{ 1680, 1050, 85, 0 },
70362306a36Sopenharmony_ci	{ 1600, 1200, 60, 0 },
70462306a36Sopenharmony_ci	{ 1600, 1200, 65, 0 },
70562306a36Sopenharmony_ci	{ 1600, 1200, 70, 0 },
70662306a36Sopenharmony_ci	/* byte 10 */
70762306a36Sopenharmony_ci	{ 1600, 1200, 75, 0 },
70862306a36Sopenharmony_ci	{ 1600, 1200, 85, 0 },
70962306a36Sopenharmony_ci	{ 1792, 1344, 60, 0 },
71062306a36Sopenharmony_ci	{ 1792, 1344, 75, 0 },
71162306a36Sopenharmony_ci	{ 1856, 1392, 60, 0 },
71262306a36Sopenharmony_ci	{ 1856, 1392, 75, 0 },
71362306a36Sopenharmony_ci	{ 1920, 1200, 60, 1 },
71462306a36Sopenharmony_ci	{ 1920, 1200, 60, 0 },
71562306a36Sopenharmony_ci	/* byte 11 */
71662306a36Sopenharmony_ci	{ 1920, 1200, 75, 0 },
71762306a36Sopenharmony_ci	{ 1920, 1200, 85, 0 },
71862306a36Sopenharmony_ci	{ 1920, 1440, 60, 0 },
71962306a36Sopenharmony_ci	{ 1920, 1440, 75, 0 },
72062306a36Sopenharmony_ci};
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic const struct minimode extra_modes[] = {
72362306a36Sopenharmony_ci	{ 1024, 576,  60, 0 },
72462306a36Sopenharmony_ci	{ 1366, 768,  60, 0 },
72562306a36Sopenharmony_ci	{ 1600, 900,  60, 0 },
72662306a36Sopenharmony_ci	{ 1680, 945,  60, 0 },
72762306a36Sopenharmony_ci	{ 1920, 1080, 60, 0 },
72862306a36Sopenharmony_ci	{ 2048, 1152, 60, 0 },
72962306a36Sopenharmony_ci	{ 2048, 1536, 60, 0 },
73062306a36Sopenharmony_ci};
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/*
73362306a36Sopenharmony_ci * From CEA/CTA-861 spec.
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * Do not access directly, instead always use cea_mode_for_vic().
73662306a36Sopenharmony_ci */
73762306a36Sopenharmony_cistatic const struct drm_display_mode edid_cea_modes_1[] = {
73862306a36Sopenharmony_ci	/* 1 - 640x480@60Hz 4:3 */
73962306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
74062306a36Sopenharmony_ci		   752, 800, 0, 480, 490, 492, 525, 0,
74162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
74262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
74362306a36Sopenharmony_ci	/* 2 - 720x480@60Hz 4:3 */
74462306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
74562306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
74662306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
74762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
74862306a36Sopenharmony_ci	/* 3 - 720x480@60Hz 16:9 */
74962306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
75062306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
75162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
75262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
75362306a36Sopenharmony_ci	/* 4 - 1280x720@60Hz 16:9 */
75462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
75562306a36Sopenharmony_ci		   1430, 1650, 0, 720, 725, 730, 750, 0,
75662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
75762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
75862306a36Sopenharmony_ci	/* 5 - 1920x1080i@60Hz 16:9 */
75962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
76062306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
76162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
76262306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
76362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
76462306a36Sopenharmony_ci	/* 6 - 720(1440)x480i@60Hz 4:3 */
76562306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
76662306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
76762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
76862306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
76962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
77062306a36Sopenharmony_ci	/* 7 - 720(1440)x480i@60Hz 16:9 */
77162306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
77262306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
77362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
77462306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
77562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
77662306a36Sopenharmony_ci	/* 8 - 720(1440)x240@60Hz 4:3 */
77762306a36Sopenharmony_ci	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
77862306a36Sopenharmony_ci		   801, 858, 0, 240, 244, 247, 262, 0,
77962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
78062306a36Sopenharmony_ci		   DRM_MODE_FLAG_DBLCLK),
78162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
78262306a36Sopenharmony_ci	/* 9 - 720(1440)x240@60Hz 16:9 */
78362306a36Sopenharmony_ci	{ DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
78462306a36Sopenharmony_ci		   801, 858, 0, 240, 244, 247, 262, 0,
78562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
78662306a36Sopenharmony_ci		   DRM_MODE_FLAG_DBLCLK),
78762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
78862306a36Sopenharmony_ci	/* 10 - 2880x480i@60Hz 4:3 */
78962306a36Sopenharmony_ci	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
79062306a36Sopenharmony_ci		   3204, 3432, 0, 480, 488, 494, 525, 0,
79162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
79262306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
79362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
79462306a36Sopenharmony_ci	/* 11 - 2880x480i@60Hz 16:9 */
79562306a36Sopenharmony_ci	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
79662306a36Sopenharmony_ci		   3204, 3432, 0, 480, 488, 494, 525, 0,
79762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
79862306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
79962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
80062306a36Sopenharmony_ci	/* 12 - 2880x240@60Hz 4:3 */
80162306a36Sopenharmony_ci	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
80262306a36Sopenharmony_ci		   3204, 3432, 0, 240, 244, 247, 262, 0,
80362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
80462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
80562306a36Sopenharmony_ci	/* 13 - 2880x240@60Hz 16:9 */
80662306a36Sopenharmony_ci	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
80762306a36Sopenharmony_ci		   3204, 3432, 0, 240, 244, 247, 262, 0,
80862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
80962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
81062306a36Sopenharmony_ci	/* 14 - 1440x480@60Hz 4:3 */
81162306a36Sopenharmony_ci	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
81262306a36Sopenharmony_ci		   1596, 1716, 0, 480, 489, 495, 525, 0,
81362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
81462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
81562306a36Sopenharmony_ci	/* 15 - 1440x480@60Hz 16:9 */
81662306a36Sopenharmony_ci	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
81762306a36Sopenharmony_ci		   1596, 1716, 0, 480, 489, 495, 525, 0,
81862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
81962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
82062306a36Sopenharmony_ci	/* 16 - 1920x1080@60Hz 16:9 */
82162306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
82262306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
82362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
82462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
82562306a36Sopenharmony_ci	/* 17 - 720x576@50Hz 4:3 */
82662306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
82762306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
82862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
82962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
83062306a36Sopenharmony_ci	/* 18 - 720x576@50Hz 16:9 */
83162306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
83262306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
83362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
83462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
83562306a36Sopenharmony_ci	/* 19 - 1280x720@50Hz 16:9 */
83662306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
83762306a36Sopenharmony_ci		   1760, 1980, 0, 720, 725, 730, 750, 0,
83862306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
83962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
84062306a36Sopenharmony_ci	/* 20 - 1920x1080i@50Hz 16:9 */
84162306a36Sopenharmony_ci	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
84262306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
84362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
84462306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
84562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
84662306a36Sopenharmony_ci	/* 21 - 720(1440)x576i@50Hz 4:3 */
84762306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
84862306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
84962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
85062306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
85162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
85262306a36Sopenharmony_ci	/* 22 - 720(1440)x576i@50Hz 16:9 */
85362306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
85462306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
85562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
85662306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
85762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
85862306a36Sopenharmony_ci	/* 23 - 720(1440)x288@50Hz 4:3 */
85962306a36Sopenharmony_ci	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
86062306a36Sopenharmony_ci		   795, 864, 0, 288, 290, 293, 312, 0,
86162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
86262306a36Sopenharmony_ci		   DRM_MODE_FLAG_DBLCLK),
86362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
86462306a36Sopenharmony_ci	/* 24 - 720(1440)x288@50Hz 16:9 */
86562306a36Sopenharmony_ci	{ DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
86662306a36Sopenharmony_ci		   795, 864, 0, 288, 290, 293, 312, 0,
86762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
86862306a36Sopenharmony_ci		   DRM_MODE_FLAG_DBLCLK),
86962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
87062306a36Sopenharmony_ci	/* 25 - 2880x576i@50Hz 4:3 */
87162306a36Sopenharmony_ci	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
87262306a36Sopenharmony_ci		   3180, 3456, 0, 576, 580, 586, 625, 0,
87362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
87462306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
87562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
87662306a36Sopenharmony_ci	/* 26 - 2880x576i@50Hz 16:9 */
87762306a36Sopenharmony_ci	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
87862306a36Sopenharmony_ci		   3180, 3456, 0, 576, 580, 586, 625, 0,
87962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
88062306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
88162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
88262306a36Sopenharmony_ci	/* 27 - 2880x288@50Hz 4:3 */
88362306a36Sopenharmony_ci	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
88462306a36Sopenharmony_ci		   3180, 3456, 0, 288, 290, 293, 312, 0,
88562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
88662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
88762306a36Sopenharmony_ci	/* 28 - 2880x288@50Hz 16:9 */
88862306a36Sopenharmony_ci	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
88962306a36Sopenharmony_ci		   3180, 3456, 0, 288, 290, 293, 312, 0,
89062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
89162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
89262306a36Sopenharmony_ci	/* 29 - 1440x576@50Hz 4:3 */
89362306a36Sopenharmony_ci	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
89462306a36Sopenharmony_ci		   1592, 1728, 0, 576, 581, 586, 625, 0,
89562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
89662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
89762306a36Sopenharmony_ci	/* 30 - 1440x576@50Hz 16:9 */
89862306a36Sopenharmony_ci	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
89962306a36Sopenharmony_ci		   1592, 1728, 0, 576, 581, 586, 625, 0,
90062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
90162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
90262306a36Sopenharmony_ci	/* 31 - 1920x1080@50Hz 16:9 */
90362306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
90462306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
90562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
90662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
90762306a36Sopenharmony_ci	/* 32 - 1920x1080@24Hz 16:9 */
90862306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
90962306a36Sopenharmony_ci		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
91062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
91162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
91262306a36Sopenharmony_ci	/* 33 - 1920x1080@25Hz 16:9 */
91362306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
91462306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
91562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
91662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
91762306a36Sopenharmony_ci	/* 34 - 1920x1080@30Hz 16:9 */
91862306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
91962306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
92062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
92162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
92262306a36Sopenharmony_ci	/* 35 - 2880x480@60Hz 4:3 */
92362306a36Sopenharmony_ci	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
92462306a36Sopenharmony_ci		   3192, 3432, 0, 480, 489, 495, 525, 0,
92562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
92662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
92762306a36Sopenharmony_ci	/* 36 - 2880x480@60Hz 16:9 */
92862306a36Sopenharmony_ci	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
92962306a36Sopenharmony_ci		   3192, 3432, 0, 480, 489, 495, 525, 0,
93062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
93162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
93262306a36Sopenharmony_ci	/* 37 - 2880x576@50Hz 4:3 */
93362306a36Sopenharmony_ci	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
93462306a36Sopenharmony_ci		   3184, 3456, 0, 576, 581, 586, 625, 0,
93562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
93662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
93762306a36Sopenharmony_ci	/* 38 - 2880x576@50Hz 16:9 */
93862306a36Sopenharmony_ci	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
93962306a36Sopenharmony_ci		   3184, 3456, 0, 576, 581, 586, 625, 0,
94062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
94162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
94262306a36Sopenharmony_ci	/* 39 - 1920x1080i@50Hz 16:9 */
94362306a36Sopenharmony_ci	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
94462306a36Sopenharmony_ci		   2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
94562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
94662306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
94762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
94862306a36Sopenharmony_ci	/* 40 - 1920x1080i@100Hz 16:9 */
94962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
95062306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
95162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
95262306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
95362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
95462306a36Sopenharmony_ci	/* 41 - 1280x720@100Hz 16:9 */
95562306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
95662306a36Sopenharmony_ci		   1760, 1980, 0, 720, 725, 730, 750, 0,
95762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
95862306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
95962306a36Sopenharmony_ci	/* 42 - 720x576@100Hz 4:3 */
96062306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
96162306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
96262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
96362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
96462306a36Sopenharmony_ci	/* 43 - 720x576@100Hz 16:9 */
96562306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
96662306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
96762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
96862306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
96962306a36Sopenharmony_ci	/* 44 - 720(1440)x576i@100Hz 4:3 */
97062306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
97162306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
97262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
97362306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
97462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
97562306a36Sopenharmony_ci	/* 45 - 720(1440)x576i@100Hz 16:9 */
97662306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
97762306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
97862306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
97962306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
98062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
98162306a36Sopenharmony_ci	/* 46 - 1920x1080i@120Hz 16:9 */
98262306a36Sopenharmony_ci	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
98362306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
98462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
98562306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE),
98662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
98762306a36Sopenharmony_ci	/* 47 - 1280x720@120Hz 16:9 */
98862306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
98962306a36Sopenharmony_ci		   1430, 1650, 0, 720, 725, 730, 750, 0,
99062306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
99162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
99262306a36Sopenharmony_ci	/* 48 - 720x480@120Hz 4:3 */
99362306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
99462306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
99562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
99662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
99762306a36Sopenharmony_ci	/* 49 - 720x480@120Hz 16:9 */
99862306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
99962306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
100062306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
100162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
100262306a36Sopenharmony_ci	/* 50 - 720(1440)x480i@120Hz 4:3 */
100362306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
100462306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
100562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
100662306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
100762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
100862306a36Sopenharmony_ci	/* 51 - 720(1440)x480i@120Hz 16:9 */
100962306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
101062306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
101162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
101262306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
101362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
101462306a36Sopenharmony_ci	/* 52 - 720x576@200Hz 4:3 */
101562306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
101662306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
101762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
101862306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
101962306a36Sopenharmony_ci	/* 53 - 720x576@200Hz 16:9 */
102062306a36Sopenharmony_ci	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
102162306a36Sopenharmony_ci		   796, 864, 0, 576, 581, 586, 625, 0,
102262306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
102362306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
102462306a36Sopenharmony_ci	/* 54 - 720(1440)x576i@200Hz 4:3 */
102562306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
102662306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
102762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
102862306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
102962306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
103062306a36Sopenharmony_ci	/* 55 - 720(1440)x576i@200Hz 16:9 */
103162306a36Sopenharmony_ci	{ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
103262306a36Sopenharmony_ci		   795, 864, 0, 576, 580, 586, 625, 0,
103362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
103462306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
103562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
103662306a36Sopenharmony_ci	/* 56 - 720x480@240Hz 4:3 */
103762306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
103862306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
103962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
104062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
104162306a36Sopenharmony_ci	/* 57 - 720x480@240Hz 16:9 */
104262306a36Sopenharmony_ci	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
104362306a36Sopenharmony_ci		   798, 858, 0, 480, 489, 495, 525, 0,
104462306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
104562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
104662306a36Sopenharmony_ci	/* 58 - 720(1440)x480i@240Hz 4:3 */
104762306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
104862306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
104962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
105062306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
105162306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
105262306a36Sopenharmony_ci	/* 59 - 720(1440)x480i@240Hz 16:9 */
105362306a36Sopenharmony_ci	{ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
105462306a36Sopenharmony_ci		   801, 858, 0, 480, 488, 494, 525, 0,
105562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
105662306a36Sopenharmony_ci		   DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
105762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
105862306a36Sopenharmony_ci	/* 60 - 1280x720@24Hz 16:9 */
105962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
106062306a36Sopenharmony_ci		   3080, 3300, 0, 720, 725, 730, 750, 0,
106162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
106262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
106362306a36Sopenharmony_ci	/* 61 - 1280x720@25Hz 16:9 */
106462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
106562306a36Sopenharmony_ci		   3740, 3960, 0, 720, 725, 730, 750, 0,
106662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
106762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
106862306a36Sopenharmony_ci	/* 62 - 1280x720@30Hz 16:9 */
106962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
107062306a36Sopenharmony_ci		   3080, 3300, 0, 720, 725, 730, 750, 0,
107162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
107262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
107362306a36Sopenharmony_ci	/* 63 - 1920x1080@120Hz 16:9 */
107462306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
107562306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
107662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
107762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
107862306a36Sopenharmony_ci	/* 64 - 1920x1080@100Hz 16:9 */
107962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
108062306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
108162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
108262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
108362306a36Sopenharmony_ci	/* 65 - 1280x720@24Hz 64:27 */
108462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
108562306a36Sopenharmony_ci		   3080, 3300, 0, 720, 725, 730, 750, 0,
108662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
108762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
108862306a36Sopenharmony_ci	/* 66 - 1280x720@25Hz 64:27 */
108962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
109062306a36Sopenharmony_ci		   3740, 3960, 0, 720, 725, 730, 750, 0,
109162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
109262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
109362306a36Sopenharmony_ci	/* 67 - 1280x720@30Hz 64:27 */
109462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
109562306a36Sopenharmony_ci		   3080, 3300, 0, 720, 725, 730, 750, 0,
109662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
109762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
109862306a36Sopenharmony_ci	/* 68 - 1280x720@50Hz 64:27 */
109962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
110062306a36Sopenharmony_ci		   1760, 1980, 0, 720, 725, 730, 750, 0,
110162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
110262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
110362306a36Sopenharmony_ci	/* 69 - 1280x720@60Hz 64:27 */
110462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
110562306a36Sopenharmony_ci		   1430, 1650, 0, 720, 725, 730, 750, 0,
110662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
110762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
110862306a36Sopenharmony_ci	/* 70 - 1280x720@100Hz 64:27 */
110962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
111062306a36Sopenharmony_ci		   1760, 1980, 0, 720, 725, 730, 750, 0,
111162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
111262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
111362306a36Sopenharmony_ci	/* 71 - 1280x720@120Hz 64:27 */
111462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
111562306a36Sopenharmony_ci		   1430, 1650, 0, 720, 725, 730, 750, 0,
111662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
111762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
111862306a36Sopenharmony_ci	/* 72 - 1920x1080@24Hz 64:27 */
111962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
112062306a36Sopenharmony_ci		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
112162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
112262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
112362306a36Sopenharmony_ci	/* 73 - 1920x1080@25Hz 64:27 */
112462306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
112562306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
112662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
112762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
112862306a36Sopenharmony_ci	/* 74 - 1920x1080@30Hz 64:27 */
112962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
113062306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
113162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
113262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
113362306a36Sopenharmony_ci	/* 75 - 1920x1080@50Hz 64:27 */
113462306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
113562306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
113662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
113762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
113862306a36Sopenharmony_ci	/* 76 - 1920x1080@60Hz 64:27 */
113962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
114062306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
114162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
114262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
114362306a36Sopenharmony_ci	/* 77 - 1920x1080@100Hz 64:27 */
114462306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
114562306a36Sopenharmony_ci		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
114662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
114762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
114862306a36Sopenharmony_ci	/* 78 - 1920x1080@120Hz 64:27 */
114962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
115062306a36Sopenharmony_ci		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
115162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
115262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
115362306a36Sopenharmony_ci	/* 79 - 1680x720@24Hz 64:27 */
115462306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
115562306a36Sopenharmony_ci		   3080, 3300, 0, 720, 725, 730, 750, 0,
115662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
115762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
115862306a36Sopenharmony_ci	/* 80 - 1680x720@25Hz 64:27 */
115962306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
116062306a36Sopenharmony_ci		   2948, 3168, 0, 720, 725, 730, 750, 0,
116162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
116262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
116362306a36Sopenharmony_ci	/* 81 - 1680x720@30Hz 64:27 */
116462306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
116562306a36Sopenharmony_ci		   2420, 2640, 0, 720, 725, 730, 750, 0,
116662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
116762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
116862306a36Sopenharmony_ci	/* 82 - 1680x720@50Hz 64:27 */
116962306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
117062306a36Sopenharmony_ci		   1980, 2200, 0, 720, 725, 730, 750, 0,
117162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
117262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
117362306a36Sopenharmony_ci	/* 83 - 1680x720@60Hz 64:27 */
117462306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
117562306a36Sopenharmony_ci		   1980, 2200, 0, 720, 725, 730, 750, 0,
117662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
117762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
117862306a36Sopenharmony_ci	/* 84 - 1680x720@100Hz 64:27 */
117962306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
118062306a36Sopenharmony_ci		   1780, 2000, 0, 720, 725, 730, 825, 0,
118162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
118262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
118362306a36Sopenharmony_ci	/* 85 - 1680x720@120Hz 64:27 */
118462306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
118562306a36Sopenharmony_ci		   1780, 2000, 0, 720, 725, 730, 825, 0,
118662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
118762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
118862306a36Sopenharmony_ci	/* 86 - 2560x1080@24Hz 64:27 */
118962306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
119062306a36Sopenharmony_ci		   3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
119162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
119262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
119362306a36Sopenharmony_ci	/* 87 - 2560x1080@25Hz 64:27 */
119462306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
119562306a36Sopenharmony_ci		   3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
119662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
119762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
119862306a36Sopenharmony_ci	/* 88 - 2560x1080@30Hz 64:27 */
119962306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
120062306a36Sopenharmony_ci		   3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
120162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
120262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
120362306a36Sopenharmony_ci	/* 89 - 2560x1080@50Hz 64:27 */
120462306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
120562306a36Sopenharmony_ci		   3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
120662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
120762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
120862306a36Sopenharmony_ci	/* 90 - 2560x1080@60Hz 64:27 */
120962306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
121062306a36Sopenharmony_ci		   2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
121162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
121262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
121362306a36Sopenharmony_ci	/* 91 - 2560x1080@100Hz 64:27 */
121462306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
121562306a36Sopenharmony_ci		   2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
121662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
121762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
121862306a36Sopenharmony_ci	/* 92 - 2560x1080@120Hz 64:27 */
121962306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
122062306a36Sopenharmony_ci		   3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
122162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
122262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
122362306a36Sopenharmony_ci	/* 93 - 3840x2160@24Hz 16:9 */
122462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
122562306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
122662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
122762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
122862306a36Sopenharmony_ci	/* 94 - 3840x2160@25Hz 16:9 */
122962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
123062306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
123162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
123262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
123362306a36Sopenharmony_ci	/* 95 - 3840x2160@30Hz 16:9 */
123462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
123562306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
123662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
123762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
123862306a36Sopenharmony_ci	/* 96 - 3840x2160@50Hz 16:9 */
123962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
124062306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
124162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
124262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
124362306a36Sopenharmony_ci	/* 97 - 3840x2160@60Hz 16:9 */
124462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
124562306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
124662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
124762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
124862306a36Sopenharmony_ci	/* 98 - 4096x2160@24Hz 256:135 */
124962306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
125062306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
125162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
125262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
125362306a36Sopenharmony_ci	/* 99 - 4096x2160@25Hz 256:135 */
125462306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
125562306a36Sopenharmony_ci		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
125662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
125762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
125862306a36Sopenharmony_ci	/* 100 - 4096x2160@30Hz 256:135 */
125962306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
126062306a36Sopenharmony_ci		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
126162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
126262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
126362306a36Sopenharmony_ci	/* 101 - 4096x2160@50Hz 256:135 */
126462306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
126562306a36Sopenharmony_ci		   5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
126662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
126762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
126862306a36Sopenharmony_ci	/* 102 - 4096x2160@60Hz 256:135 */
126962306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
127062306a36Sopenharmony_ci		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
127162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
127262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
127362306a36Sopenharmony_ci	/* 103 - 3840x2160@24Hz 64:27 */
127462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
127562306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
127662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
127762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
127862306a36Sopenharmony_ci	/* 104 - 3840x2160@25Hz 64:27 */
127962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
128062306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
128162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
128262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
128362306a36Sopenharmony_ci	/* 105 - 3840x2160@30Hz 64:27 */
128462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
128562306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
128662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
128762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
128862306a36Sopenharmony_ci	/* 106 - 3840x2160@50Hz 64:27 */
128962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
129062306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
129162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
129262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
129362306a36Sopenharmony_ci	/* 107 - 3840x2160@60Hz 64:27 */
129462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
129562306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
129662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
129762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
129862306a36Sopenharmony_ci	/* 108 - 1280x720@48Hz 16:9 */
129962306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
130062306a36Sopenharmony_ci		   2280, 2500, 0, 720, 725, 730, 750, 0,
130162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
130262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
130362306a36Sopenharmony_ci	/* 109 - 1280x720@48Hz 64:27 */
130462306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 90000, 1280, 2240,
130562306a36Sopenharmony_ci		   2280, 2500, 0, 720, 725, 730, 750, 0,
130662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
130762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
130862306a36Sopenharmony_ci	/* 110 - 1680x720@48Hz 64:27 */
130962306a36Sopenharmony_ci	{ DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 2490,
131062306a36Sopenharmony_ci		   2530, 2750, 0, 720, 725, 730, 750, 0,
131162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
131262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
131362306a36Sopenharmony_ci	/* 111 - 1920x1080@48Hz 16:9 */
131462306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
131562306a36Sopenharmony_ci		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
131662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
131762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
131862306a36Sopenharmony_ci	/* 112 - 1920x1080@48Hz 64:27 */
131962306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2558,
132062306a36Sopenharmony_ci		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
132162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
132262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
132362306a36Sopenharmony_ci	/* 113 - 2560x1080@48Hz 64:27 */
132462306a36Sopenharmony_ci	{ DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 3558,
132562306a36Sopenharmony_ci		   3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
132662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
132762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
132862306a36Sopenharmony_ci	/* 114 - 3840x2160@48Hz 16:9 */
132962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
133062306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
133162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
133262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
133362306a36Sopenharmony_ci	/* 115 - 4096x2160@48Hz 256:135 */
133462306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5116,
133562306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
133662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
133762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
133862306a36Sopenharmony_ci	/* 116 - 3840x2160@48Hz 64:27 */
133962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 5116,
134062306a36Sopenharmony_ci		   5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
134162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
134262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
134362306a36Sopenharmony_ci	/* 117 - 3840x2160@100Hz 16:9 */
134462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
134562306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
134662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
134762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
134862306a36Sopenharmony_ci	/* 118 - 3840x2160@120Hz 16:9 */
134962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
135062306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
135162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
135262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
135362306a36Sopenharmony_ci	/* 119 - 3840x2160@100Hz 64:27 */
135462306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4896,
135562306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
135662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
135762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
135862306a36Sopenharmony_ci	/* 120 - 3840x2160@120Hz 64:27 */
135962306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 1188000, 3840, 4016,
136062306a36Sopenharmony_ci		   4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
136162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
136262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
136362306a36Sopenharmony_ci	/* 121 - 5120x2160@24Hz 64:27 */
136462306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 7116,
136562306a36Sopenharmony_ci		   7204, 7500, 0, 2160, 2168, 2178, 2200, 0,
136662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
136762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
136862306a36Sopenharmony_ci	/* 122 - 5120x2160@25Hz 64:27 */
136962306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 6816,
137062306a36Sopenharmony_ci		   6904, 7200, 0, 2160, 2168, 2178, 2200, 0,
137162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
137262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
137362306a36Sopenharmony_ci	/* 123 - 5120x2160@30Hz 64:27 */
137462306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 396000, 5120, 5784,
137562306a36Sopenharmony_ci		   5872, 6000, 0, 2160, 2168, 2178, 2200, 0,
137662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
137762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
137862306a36Sopenharmony_ci	/* 124 - 5120x2160@48Hz 64:27 */
137962306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5866,
138062306a36Sopenharmony_ci		   5954, 6250, 0, 2160, 2168, 2178, 2475, 0,
138162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
138262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
138362306a36Sopenharmony_ci	/* 125 - 5120x2160@50Hz 64:27 */
138462306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 6216,
138562306a36Sopenharmony_ci		   6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
138662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
138762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
138862306a36Sopenharmony_ci	/* 126 - 5120x2160@60Hz 64:27 */
138962306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 742500, 5120, 5284,
139062306a36Sopenharmony_ci		   5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
139162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
139262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
139362306a36Sopenharmony_ci	/* 127 - 5120x2160@100Hz 64:27 */
139462306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 6216,
139562306a36Sopenharmony_ci		   6304, 6600, 0, 2160, 2168, 2178, 2250, 0,
139662306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
139762306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
139862306a36Sopenharmony_ci};
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci/*
140162306a36Sopenharmony_ci * From CEA/CTA-861 spec.
140262306a36Sopenharmony_ci *
140362306a36Sopenharmony_ci * Do not access directly, instead always use cea_mode_for_vic().
140462306a36Sopenharmony_ci */
140562306a36Sopenharmony_cistatic const struct drm_display_mode edid_cea_modes_193[] = {
140662306a36Sopenharmony_ci	/* 193 - 5120x2160@120Hz 64:27 */
140762306a36Sopenharmony_ci	{ DRM_MODE("5120x2160", DRM_MODE_TYPE_DRIVER, 1485000, 5120, 5284,
140862306a36Sopenharmony_ci		   5372, 5500, 0, 2160, 2168, 2178, 2250, 0,
140962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
141062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
141162306a36Sopenharmony_ci	/* 194 - 7680x4320@24Hz 16:9 */
141262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10232,
141362306a36Sopenharmony_ci		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
141462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
141562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
141662306a36Sopenharmony_ci	/* 195 - 7680x4320@25Hz 16:9 */
141762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10032,
141862306a36Sopenharmony_ci		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
141962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
142062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
142162306a36Sopenharmony_ci	/* 196 - 7680x4320@30Hz 16:9 */
142262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 8232,
142362306a36Sopenharmony_ci		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
142462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
142562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
142662306a36Sopenharmony_ci	/* 197 - 7680x4320@48Hz 16:9 */
142762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10232,
142862306a36Sopenharmony_ci		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
142962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
143062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
143162306a36Sopenharmony_ci	/* 198 - 7680x4320@50Hz 16:9 */
143262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10032,
143362306a36Sopenharmony_ci		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
143462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
143562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
143662306a36Sopenharmony_ci	/* 199 - 7680x4320@60Hz 16:9 */
143762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 8232,
143862306a36Sopenharmony_ci		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
143962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
144062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
144162306a36Sopenharmony_ci	/* 200 - 7680x4320@100Hz 16:9 */
144262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 9792,
144362306a36Sopenharmony_ci		   9968, 10560, 0, 4320, 4336, 4356, 4500, 0,
144462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
144562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
144662306a36Sopenharmony_ci	/* 201 - 7680x4320@120Hz 16:9 */
144762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 8032,
144862306a36Sopenharmony_ci		   8208, 8800, 0, 4320, 4336, 4356, 4500, 0,
144962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
145062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
145162306a36Sopenharmony_ci	/* 202 - 7680x4320@24Hz 64:27 */
145262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10232,
145362306a36Sopenharmony_ci		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
145462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
145562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
145662306a36Sopenharmony_ci	/* 203 - 7680x4320@25Hz 64:27 */
145762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 10032,
145862306a36Sopenharmony_ci		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
145962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
146062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
146162306a36Sopenharmony_ci	/* 204 - 7680x4320@30Hz 64:27 */
146262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 1188000, 7680, 8232,
146362306a36Sopenharmony_ci		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
146462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
146562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
146662306a36Sopenharmony_ci	/* 205 - 7680x4320@48Hz 64:27 */
146762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10232,
146862306a36Sopenharmony_ci		   10408, 11000, 0, 4320, 4336, 4356, 4500, 0,
146962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
147062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
147162306a36Sopenharmony_ci	/* 206 - 7680x4320@50Hz 64:27 */
147262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 10032,
147362306a36Sopenharmony_ci		   10208, 10800, 0, 4320, 4336, 4356, 4400, 0,
147462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
147562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
147662306a36Sopenharmony_ci	/* 207 - 7680x4320@60Hz 64:27 */
147762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 2376000, 7680, 8232,
147862306a36Sopenharmony_ci		   8408, 9000, 0, 4320, 4336, 4356, 4400, 0,
147962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
148062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
148162306a36Sopenharmony_ci	/* 208 - 7680x4320@100Hz 64:27 */
148262306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 9792,
148362306a36Sopenharmony_ci		   9968, 10560, 0, 4320, 4336, 4356, 4500, 0,
148462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
148562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
148662306a36Sopenharmony_ci	/* 209 - 7680x4320@120Hz 64:27 */
148762306a36Sopenharmony_ci	{ DRM_MODE("7680x4320", DRM_MODE_TYPE_DRIVER, 4752000, 7680, 8032,
148862306a36Sopenharmony_ci		   8208, 8800, 0, 4320, 4336, 4356, 4500, 0,
148962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
149062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
149162306a36Sopenharmony_ci	/* 210 - 10240x4320@24Hz 64:27 */
149262306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 11732,
149362306a36Sopenharmony_ci		   11908, 12500, 0, 4320, 4336, 4356, 4950, 0,
149462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
149562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
149662306a36Sopenharmony_ci	/* 211 - 10240x4320@25Hz 64:27 */
149762306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 12732,
149862306a36Sopenharmony_ci		   12908, 13500, 0, 4320, 4336, 4356, 4400, 0,
149962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
150062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
150162306a36Sopenharmony_ci	/* 212 - 10240x4320@30Hz 64:27 */
150262306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 1485000, 10240, 10528,
150362306a36Sopenharmony_ci		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
150462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
150562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
150662306a36Sopenharmony_ci	/* 213 - 10240x4320@48Hz 64:27 */
150762306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 11732,
150862306a36Sopenharmony_ci		   11908, 12500, 0, 4320, 4336, 4356, 4950, 0,
150962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
151062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
151162306a36Sopenharmony_ci	/* 214 - 10240x4320@50Hz 64:27 */
151262306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 12732,
151362306a36Sopenharmony_ci		   12908, 13500, 0, 4320, 4336, 4356, 4400, 0,
151462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
151562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
151662306a36Sopenharmony_ci	/* 215 - 10240x4320@60Hz 64:27 */
151762306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 2970000, 10240, 10528,
151862306a36Sopenharmony_ci		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
151962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
152062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
152162306a36Sopenharmony_ci	/* 216 - 10240x4320@100Hz 64:27 */
152262306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 5940000, 10240, 12432,
152362306a36Sopenharmony_ci		   12608, 13200, 0, 4320, 4336, 4356, 4500, 0,
152462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
152562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
152662306a36Sopenharmony_ci	/* 217 - 10240x4320@120Hz 64:27 */
152762306a36Sopenharmony_ci	{ DRM_MODE("10240x4320", DRM_MODE_TYPE_DRIVER, 5940000, 10240, 10528,
152862306a36Sopenharmony_ci		   10704, 11000, 0, 4320, 4336, 4356, 4500, 0,
152962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
153062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
153162306a36Sopenharmony_ci	/* 218 - 4096x2160@100Hz 256:135 */
153262306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 1188000, 4096, 4896,
153362306a36Sopenharmony_ci		   4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
153462306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
153562306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
153662306a36Sopenharmony_ci	/* 219 - 4096x2160@120Hz 256:135 */
153762306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 1188000, 4096, 4184,
153862306a36Sopenharmony_ci		   4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
153962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
154062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
154162306a36Sopenharmony_ci};
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci/*
154462306a36Sopenharmony_ci * HDMI 1.4 4k modes. Index using the VIC.
154562306a36Sopenharmony_ci */
154662306a36Sopenharmony_cistatic const struct drm_display_mode edid_4k_modes[] = {
154762306a36Sopenharmony_ci	/* 0 - dummy, VICs start at 1 */
154862306a36Sopenharmony_ci	{ },
154962306a36Sopenharmony_ci	/* 1 - 3840x2160@30Hz */
155062306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
155162306a36Sopenharmony_ci		   3840, 4016, 4104, 4400, 0,
155262306a36Sopenharmony_ci		   2160, 2168, 2178, 2250, 0,
155362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
155462306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
155562306a36Sopenharmony_ci	/* 2 - 3840x2160@25Hz */
155662306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
155762306a36Sopenharmony_ci		   3840, 4896, 4984, 5280, 0,
155862306a36Sopenharmony_ci		   2160, 2168, 2178, 2250, 0,
155962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
156062306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
156162306a36Sopenharmony_ci	/* 3 - 3840x2160@24Hz */
156262306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
156362306a36Sopenharmony_ci		   3840, 5116, 5204, 5500, 0,
156462306a36Sopenharmony_ci		   2160, 2168, 2178, 2250, 0,
156562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
156662306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
156762306a36Sopenharmony_ci	/* 4 - 4096x2160@24Hz (SMPTE) */
156862306a36Sopenharmony_ci	{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000,
156962306a36Sopenharmony_ci		   4096, 5116, 5204, 5500, 0,
157062306a36Sopenharmony_ci		   2160, 2168, 2178, 2250, 0,
157162306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
157262306a36Sopenharmony_ci	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, },
157362306a36Sopenharmony_ci};
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci/*** DDC fetch and block validation ***/
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci/*
157862306a36Sopenharmony_ci * The opaque EDID type, internal to drm_edid.c.
157962306a36Sopenharmony_ci */
158062306a36Sopenharmony_cistruct drm_edid {
158162306a36Sopenharmony_ci	/* Size allocated for edid */
158262306a36Sopenharmony_ci	size_t size;
158362306a36Sopenharmony_ci	const struct edid *edid;
158462306a36Sopenharmony_ci};
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_cistatic int edid_hfeeodb_extension_block_count(const struct edid *edid);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic int edid_hfeeodb_block_count(const struct edid *edid)
158962306a36Sopenharmony_ci{
159062306a36Sopenharmony_ci	int eeodb = edid_hfeeodb_extension_block_count(edid);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	return eeodb ? eeodb + 1 : 0;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cistatic int edid_extension_block_count(const struct edid *edid)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	return edid->extensions;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic int edid_block_count(const struct edid *edid)
160162306a36Sopenharmony_ci{
160262306a36Sopenharmony_ci	return edid_extension_block_count(edid) + 1;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int edid_size_by_blocks(int num_blocks)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	return num_blocks * EDID_LENGTH;
160862306a36Sopenharmony_ci}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_cistatic int edid_size(const struct edid *edid)
161162306a36Sopenharmony_ci{
161262306a36Sopenharmony_ci	return edid_size_by_blocks(edid_block_count(edid));
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistatic const void *edid_block_data(const struct edid *edid, int index)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	return edid + index;
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic const void *edid_extension_block_data(const struct edid *edid, int index)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	return edid_block_data(edid, index + 1);
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci/* EDID block count indicated in EDID, may exceed allocated size */
162862306a36Sopenharmony_cistatic int __drm_edid_block_count(const struct drm_edid *drm_edid)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	int num_blocks;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* Starting point */
163362306a36Sopenharmony_ci	num_blocks = edid_block_count(drm_edid->edid);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/* HF-EEODB override */
163662306a36Sopenharmony_ci	if (drm_edid->size >= edid_size_by_blocks(2)) {
163762306a36Sopenharmony_ci		int eeodb;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci		/*
164062306a36Sopenharmony_ci		 * Note: HF-EEODB may specify a smaller extension count than the
164162306a36Sopenharmony_ci		 * regular one. Unlike in buffer allocation, here we can use it.
164262306a36Sopenharmony_ci		 */
164362306a36Sopenharmony_ci		eeodb = edid_hfeeodb_block_count(drm_edid->edid);
164462306a36Sopenharmony_ci		if (eeodb)
164562306a36Sopenharmony_ci			num_blocks = eeodb;
164662306a36Sopenharmony_ci	}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	return num_blocks;
164962306a36Sopenharmony_ci}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci/* EDID block count, limited by allocated size */
165262306a36Sopenharmony_cistatic int drm_edid_block_count(const struct drm_edid *drm_edid)
165362306a36Sopenharmony_ci{
165462306a36Sopenharmony_ci	/* Limit by allocated size */
165562306a36Sopenharmony_ci	return min(__drm_edid_block_count(drm_edid),
165662306a36Sopenharmony_ci		   (int)drm_edid->size / EDID_LENGTH);
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci/* EDID extension block count, limited by allocated size */
166062306a36Sopenharmony_cistatic int drm_edid_extension_block_count(const struct drm_edid *drm_edid)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	return drm_edid_block_count(drm_edid) - 1;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic const void *drm_edid_block_data(const struct drm_edid *drm_edid, int index)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	return edid_block_data(drm_edid->edid, index);
166862306a36Sopenharmony_ci}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_cistatic const void *drm_edid_extension_block_data(const struct drm_edid *drm_edid,
167162306a36Sopenharmony_ci						 int index)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	return edid_extension_block_data(drm_edid->edid, index);
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci/*
167762306a36Sopenharmony_ci * Initializer helper for legacy interfaces, where we have no choice but to
167862306a36Sopenharmony_ci * trust edid size. Not for general purpose use.
167962306a36Sopenharmony_ci */
168062306a36Sopenharmony_cistatic const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid,
168162306a36Sopenharmony_ci						   const struct edid *edid)
168262306a36Sopenharmony_ci{
168362306a36Sopenharmony_ci	if (!edid)
168462306a36Sopenharmony_ci		return NULL;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	memset(drm_edid, 0, sizeof(*drm_edid));
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	drm_edid->edid = edid;
168962306a36Sopenharmony_ci	drm_edid->size = edid_size(edid);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	return drm_edid;
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci/*
169562306a36Sopenharmony_ci * EDID base and extension block iterator.
169662306a36Sopenharmony_ci *
169762306a36Sopenharmony_ci * struct drm_edid_iter iter;
169862306a36Sopenharmony_ci * const u8 *block;
169962306a36Sopenharmony_ci *
170062306a36Sopenharmony_ci * drm_edid_iter_begin(drm_edid, &iter);
170162306a36Sopenharmony_ci * drm_edid_iter_for_each(block, &iter) {
170262306a36Sopenharmony_ci *         // do stuff with block
170362306a36Sopenharmony_ci * }
170462306a36Sopenharmony_ci * drm_edid_iter_end(&iter);
170562306a36Sopenharmony_ci */
170662306a36Sopenharmony_cistruct drm_edid_iter {
170762306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	/* Current block index. */
171062306a36Sopenharmony_ci	int index;
171162306a36Sopenharmony_ci};
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cistatic void drm_edid_iter_begin(const struct drm_edid *drm_edid,
171462306a36Sopenharmony_ci				struct drm_edid_iter *iter)
171562306a36Sopenharmony_ci{
171662306a36Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	iter->drm_edid = drm_edid;
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cistatic const void *__drm_edid_iter_next(struct drm_edid_iter *iter)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	const void *block = NULL;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (!iter->drm_edid)
172662306a36Sopenharmony_ci		return NULL;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	if (iter->index < drm_edid_block_count(iter->drm_edid))
172962306a36Sopenharmony_ci		block = drm_edid_block_data(iter->drm_edid, iter->index++);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	return block;
173262306a36Sopenharmony_ci}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci#define drm_edid_iter_for_each(__block, __iter)			\
173562306a36Sopenharmony_ci	while (((__block) = __drm_edid_iter_next(__iter)))
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic void drm_edid_iter_end(struct drm_edid_iter *iter)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic const u8 edid_header[] = {
174362306a36Sopenharmony_ci	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
174462306a36Sopenharmony_ci};
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic void edid_header_fix(void *edid)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	memcpy(edid, edid_header, sizeof(edid_header));
174962306a36Sopenharmony_ci}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci/**
175262306a36Sopenharmony_ci * drm_edid_header_is_valid - sanity check the header of the base EDID block
175362306a36Sopenharmony_ci * @_edid: pointer to raw base EDID block
175462306a36Sopenharmony_ci *
175562306a36Sopenharmony_ci * Sanity check the header of the base EDID block.
175662306a36Sopenharmony_ci *
175762306a36Sopenharmony_ci * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
175862306a36Sopenharmony_ci */
175962306a36Sopenharmony_ciint drm_edid_header_is_valid(const void *_edid)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	const struct edid *edid = _edid;
176262306a36Sopenharmony_ci	int i, score = 0;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	for (i = 0; i < sizeof(edid_header); i++) {
176562306a36Sopenharmony_ci		if (edid->header[i] == edid_header[i])
176662306a36Sopenharmony_ci			score++;
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	return score;
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_header_is_valid);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic int edid_fixup __read_mostly = 6;
177462306a36Sopenharmony_cimodule_param_named(edid_fixup, edid_fixup, int, 0400);
177562306a36Sopenharmony_ciMODULE_PARM_DESC(edid_fixup,
177662306a36Sopenharmony_ci		 "Minimum number of valid EDID header bytes (0-8, default 6)");
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_cistatic int edid_block_compute_checksum(const void *_block)
177962306a36Sopenharmony_ci{
178062306a36Sopenharmony_ci	const u8 *block = _block;
178162306a36Sopenharmony_ci	int i;
178262306a36Sopenharmony_ci	u8 csum = 0, crc = 0;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	for (i = 0; i < EDID_LENGTH - 1; i++)
178562306a36Sopenharmony_ci		csum += block[i];
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	crc = 0x100 - csum;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	return crc;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic int edid_block_get_checksum(const void *_block)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	const struct edid *block = _block;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return block->checksum;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic int edid_block_tag(const void *_block)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	const u8 *block = _block;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	return block[0];
180462306a36Sopenharmony_ci}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_cistatic bool edid_block_is_zero(const void *edid)
180762306a36Sopenharmony_ci{
180862306a36Sopenharmony_ci	return !memchr_inv(edid, 0, EDID_LENGTH);
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci/**
181262306a36Sopenharmony_ci * drm_edid_are_equal - compare two edid blobs.
181362306a36Sopenharmony_ci * @edid1: pointer to first blob
181462306a36Sopenharmony_ci * @edid2: pointer to second blob
181562306a36Sopenharmony_ci * This helper can be used during probing to determine if
181662306a36Sopenharmony_ci * edid had changed.
181762306a36Sopenharmony_ci */
181862306a36Sopenharmony_cibool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
181962306a36Sopenharmony_ci{
182062306a36Sopenharmony_ci	int edid1_len, edid2_len;
182162306a36Sopenharmony_ci	bool edid1_present = edid1 != NULL;
182262306a36Sopenharmony_ci	bool edid2_present = edid2 != NULL;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (edid1_present != edid2_present)
182562306a36Sopenharmony_ci		return false;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (edid1) {
182862306a36Sopenharmony_ci		edid1_len = edid_size(edid1);
182962306a36Sopenharmony_ci		edid2_len = edid_size(edid2);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci		if (edid1_len != edid2_len)
183262306a36Sopenharmony_ci			return false;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		if (memcmp(edid1, edid2, edid1_len))
183562306a36Sopenharmony_ci			return false;
183662306a36Sopenharmony_ci	}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	return true;
183962306a36Sopenharmony_ci}
184062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_are_equal);
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cienum edid_block_status {
184362306a36Sopenharmony_ci	EDID_BLOCK_OK = 0,
184462306a36Sopenharmony_ci	EDID_BLOCK_READ_FAIL,
184562306a36Sopenharmony_ci	EDID_BLOCK_NULL,
184662306a36Sopenharmony_ci	EDID_BLOCK_ZERO,
184762306a36Sopenharmony_ci	EDID_BLOCK_HEADER_CORRUPT,
184862306a36Sopenharmony_ci	EDID_BLOCK_HEADER_REPAIR,
184962306a36Sopenharmony_ci	EDID_BLOCK_HEADER_FIXED,
185062306a36Sopenharmony_ci	EDID_BLOCK_CHECKSUM,
185162306a36Sopenharmony_ci	EDID_BLOCK_VERSION,
185262306a36Sopenharmony_ci};
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic enum edid_block_status edid_block_check(const void *_block,
185562306a36Sopenharmony_ci					       bool is_base_block)
185662306a36Sopenharmony_ci{
185762306a36Sopenharmony_ci	const struct edid *block = _block;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	if (!block)
186062306a36Sopenharmony_ci		return EDID_BLOCK_NULL;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (is_base_block) {
186362306a36Sopenharmony_ci		int score = drm_edid_header_is_valid(block);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci		if (score < clamp(edid_fixup, 0, 8)) {
186662306a36Sopenharmony_ci			if (edid_block_is_zero(block))
186762306a36Sopenharmony_ci				return EDID_BLOCK_ZERO;
186862306a36Sopenharmony_ci			else
186962306a36Sopenharmony_ci				return EDID_BLOCK_HEADER_CORRUPT;
187062306a36Sopenharmony_ci		}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci		if (score < 8)
187362306a36Sopenharmony_ci			return EDID_BLOCK_HEADER_REPAIR;
187462306a36Sopenharmony_ci	}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) {
187762306a36Sopenharmony_ci		if (edid_block_is_zero(block))
187862306a36Sopenharmony_ci			return EDID_BLOCK_ZERO;
187962306a36Sopenharmony_ci		else
188062306a36Sopenharmony_ci			return EDID_BLOCK_CHECKSUM;
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (is_base_block) {
188462306a36Sopenharmony_ci		if (block->version != 1)
188562306a36Sopenharmony_ci			return EDID_BLOCK_VERSION;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	return EDID_BLOCK_OK;
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_cistatic bool edid_block_status_valid(enum edid_block_status status, int tag)
189262306a36Sopenharmony_ci{
189362306a36Sopenharmony_ci	return status == EDID_BLOCK_OK ||
189462306a36Sopenharmony_ci		status == EDID_BLOCK_HEADER_FIXED ||
189562306a36Sopenharmony_ci		(status == EDID_BLOCK_CHECKSUM && tag == CEA_EXT);
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_cistatic bool edid_block_valid(const void *block, bool base)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	return edid_block_status_valid(edid_block_check(block, base),
190162306a36Sopenharmony_ci				       edid_block_tag(block));
190262306a36Sopenharmony_ci}
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_cistatic void edid_block_status_print(enum edid_block_status status,
190562306a36Sopenharmony_ci				    const struct edid *block,
190662306a36Sopenharmony_ci				    int block_num)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	switch (status) {
190962306a36Sopenharmony_ci	case EDID_BLOCK_OK:
191062306a36Sopenharmony_ci		break;
191162306a36Sopenharmony_ci	case EDID_BLOCK_READ_FAIL:
191262306a36Sopenharmony_ci		pr_debug("EDID block %d read failed\n", block_num);
191362306a36Sopenharmony_ci		break;
191462306a36Sopenharmony_ci	case EDID_BLOCK_NULL:
191562306a36Sopenharmony_ci		pr_debug("EDID block %d pointer is NULL\n", block_num);
191662306a36Sopenharmony_ci		break;
191762306a36Sopenharmony_ci	case EDID_BLOCK_ZERO:
191862306a36Sopenharmony_ci		pr_notice("EDID block %d is all zeroes\n", block_num);
191962306a36Sopenharmony_ci		break;
192062306a36Sopenharmony_ci	case EDID_BLOCK_HEADER_CORRUPT:
192162306a36Sopenharmony_ci		pr_notice("EDID has corrupt header\n");
192262306a36Sopenharmony_ci		break;
192362306a36Sopenharmony_ci	case EDID_BLOCK_HEADER_REPAIR:
192462306a36Sopenharmony_ci		pr_debug("EDID corrupt header needs repair\n");
192562306a36Sopenharmony_ci		break;
192662306a36Sopenharmony_ci	case EDID_BLOCK_HEADER_FIXED:
192762306a36Sopenharmony_ci		pr_debug("EDID corrupt header fixed\n");
192862306a36Sopenharmony_ci		break;
192962306a36Sopenharmony_ci	case EDID_BLOCK_CHECKSUM:
193062306a36Sopenharmony_ci		if (edid_block_status_valid(status, edid_block_tag(block))) {
193162306a36Sopenharmony_ci			pr_debug("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d, ignoring\n",
193262306a36Sopenharmony_ci				 block_num, edid_block_tag(block),
193362306a36Sopenharmony_ci				 edid_block_compute_checksum(block));
193462306a36Sopenharmony_ci		} else {
193562306a36Sopenharmony_ci			pr_notice("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d\n",
193662306a36Sopenharmony_ci				  block_num, edid_block_tag(block),
193762306a36Sopenharmony_ci				  edid_block_compute_checksum(block));
193862306a36Sopenharmony_ci		}
193962306a36Sopenharmony_ci		break;
194062306a36Sopenharmony_ci	case EDID_BLOCK_VERSION:
194162306a36Sopenharmony_ci		pr_notice("EDID has major version %d, instead of 1\n",
194262306a36Sopenharmony_ci			  block->version);
194362306a36Sopenharmony_ci		break;
194462306a36Sopenharmony_ci	default:
194562306a36Sopenharmony_ci		WARN(1, "EDID block %d unknown edid block status code %d\n",
194662306a36Sopenharmony_ci		     block_num, status);
194762306a36Sopenharmony_ci		break;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic void edid_block_dump(const char *level, const void *block, int block_num)
195262306a36Sopenharmony_ci{
195362306a36Sopenharmony_ci	enum edid_block_status status;
195462306a36Sopenharmony_ci	char prefix[20];
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	status = edid_block_check(block, block_num == 0);
195762306a36Sopenharmony_ci	if (status == EDID_BLOCK_ZERO)
195862306a36Sopenharmony_ci		sprintf(prefix, "\t[%02x] ZERO ", block_num);
195962306a36Sopenharmony_ci	else if (!edid_block_status_valid(status, edid_block_tag(block)))
196062306a36Sopenharmony_ci		sprintf(prefix, "\t[%02x] BAD  ", block_num);
196162306a36Sopenharmony_ci	else
196262306a36Sopenharmony_ci		sprintf(prefix, "\t[%02x] GOOD ", block_num);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
196562306a36Sopenharmony_ci		       block, EDID_LENGTH, false);
196662306a36Sopenharmony_ci}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci/**
196962306a36Sopenharmony_ci * drm_edid_block_valid - Sanity check the EDID block (base or extension)
197062306a36Sopenharmony_ci * @_block: pointer to raw EDID block
197162306a36Sopenharmony_ci * @block_num: type of block to validate (0 for base, extension otherwise)
197262306a36Sopenharmony_ci * @print_bad_edid: if true, dump bad EDID blocks to the console
197362306a36Sopenharmony_ci * @edid_corrupt: if true, the header or checksum is invalid
197462306a36Sopenharmony_ci *
197562306a36Sopenharmony_ci * Validate a base or extension EDID block and optionally dump bad blocks to
197662306a36Sopenharmony_ci * the console.
197762306a36Sopenharmony_ci *
197862306a36Sopenharmony_ci * Return: True if the block is valid, false otherwise.
197962306a36Sopenharmony_ci */
198062306a36Sopenharmony_cibool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
198162306a36Sopenharmony_ci			  bool *edid_corrupt)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	struct edid *block = (struct edid *)_block;
198462306a36Sopenharmony_ci	enum edid_block_status status;
198562306a36Sopenharmony_ci	bool is_base_block = block_num == 0;
198662306a36Sopenharmony_ci	bool valid;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (WARN_ON(!block))
198962306a36Sopenharmony_ci		return false;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	status = edid_block_check(block, is_base_block);
199262306a36Sopenharmony_ci	if (status == EDID_BLOCK_HEADER_REPAIR) {
199362306a36Sopenharmony_ci		DRM_DEBUG_KMS("Fixing EDID header, your hardware may be failing\n");
199462306a36Sopenharmony_ci		edid_header_fix(block);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci		/* Retry with fixed header, update status if that worked. */
199762306a36Sopenharmony_ci		status = edid_block_check(block, is_base_block);
199862306a36Sopenharmony_ci		if (status == EDID_BLOCK_OK)
199962306a36Sopenharmony_ci			status = EDID_BLOCK_HEADER_FIXED;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (edid_corrupt) {
200362306a36Sopenharmony_ci		/*
200462306a36Sopenharmony_ci		 * Unknown major version isn't corrupt but we can't use it. Only
200562306a36Sopenharmony_ci		 * the base block can reset edid_corrupt to false.
200662306a36Sopenharmony_ci		 */
200762306a36Sopenharmony_ci		if (is_base_block &&
200862306a36Sopenharmony_ci		    (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION))
200962306a36Sopenharmony_ci			*edid_corrupt = false;
201062306a36Sopenharmony_ci		else if (status != EDID_BLOCK_OK)
201162306a36Sopenharmony_ci			*edid_corrupt = true;
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	edid_block_status_print(status, block, block_num);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/* Determine whether we can use this block with this status. */
201762306a36Sopenharmony_ci	valid = edid_block_status_valid(status, edid_block_tag(block));
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	if (!valid && print_bad_edid && status != EDID_BLOCK_ZERO) {
202062306a36Sopenharmony_ci		pr_notice("Raw EDID:\n");
202162306a36Sopenharmony_ci		edid_block_dump(KERN_NOTICE, block, block_num);
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	return valid;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_block_valid);
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci/**
202962306a36Sopenharmony_ci * drm_edid_is_valid - sanity check EDID data
203062306a36Sopenharmony_ci * @edid: EDID data
203162306a36Sopenharmony_ci *
203262306a36Sopenharmony_ci * Sanity-check an entire EDID record (including extensions)
203362306a36Sopenharmony_ci *
203462306a36Sopenharmony_ci * Return: True if the EDID data is valid, false otherwise.
203562306a36Sopenharmony_ci */
203662306a36Sopenharmony_cibool drm_edid_is_valid(struct edid *edid)
203762306a36Sopenharmony_ci{
203862306a36Sopenharmony_ci	int i;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	if (!edid)
204162306a36Sopenharmony_ci		return false;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	for (i = 0; i < edid_block_count(edid); i++) {
204462306a36Sopenharmony_ci		void *block = (void *)edid_block_data(edid, i);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		if (!drm_edid_block_valid(block, i, true, NULL))
204762306a36Sopenharmony_ci			return false;
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	return true;
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_is_valid);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci/**
205562306a36Sopenharmony_ci * drm_edid_valid - sanity check EDID data
205662306a36Sopenharmony_ci * @drm_edid: EDID data
205762306a36Sopenharmony_ci *
205862306a36Sopenharmony_ci * Sanity check an EDID. Cross check block count against allocated size and
205962306a36Sopenharmony_ci * checksum the blocks.
206062306a36Sopenharmony_ci *
206162306a36Sopenharmony_ci * Return: True if the EDID data is valid, false otherwise.
206262306a36Sopenharmony_ci */
206362306a36Sopenharmony_cibool drm_edid_valid(const struct drm_edid *drm_edid)
206462306a36Sopenharmony_ci{
206562306a36Sopenharmony_ci	int i;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	if (!drm_edid)
206862306a36Sopenharmony_ci		return false;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	if (edid_size_by_blocks(__drm_edid_block_count(drm_edid)) != drm_edid->size)
207162306a36Sopenharmony_ci		return false;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	for (i = 0; i < drm_edid_block_count(drm_edid); i++) {
207462306a36Sopenharmony_ci		const void *block = drm_edid_block_data(drm_edid, i);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci		if (!edid_block_valid(block, i == 0))
207762306a36Sopenharmony_ci			return false;
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	return true;
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_valid);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_cistatic struct edid *edid_filter_invalid_blocks(struct edid *edid,
208562306a36Sopenharmony_ci					       size_t *alloc_size)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	struct edid *new;
208862306a36Sopenharmony_ci	int i, valid_blocks = 0;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	/*
209162306a36Sopenharmony_ci	 * Note: If the EDID uses HF-EEODB, but has invalid blocks, we'll revert
209262306a36Sopenharmony_ci	 * back to regular extension count here. We don't want to start
209362306a36Sopenharmony_ci	 * modifying the HF-EEODB extension too.
209462306a36Sopenharmony_ci	 */
209562306a36Sopenharmony_ci	for (i = 0; i < edid_block_count(edid); i++) {
209662306a36Sopenharmony_ci		const void *src_block = edid_block_data(edid, i);
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci		if (edid_block_valid(src_block, i == 0)) {
209962306a36Sopenharmony_ci			void *dst_block = (void *)edid_block_data(edid, valid_blocks);
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci			memmove(dst_block, src_block, EDID_LENGTH);
210262306a36Sopenharmony_ci			valid_blocks++;
210362306a36Sopenharmony_ci		}
210462306a36Sopenharmony_ci	}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	/* We already trusted the base block to be valid here... */
210762306a36Sopenharmony_ci	if (WARN_ON(!valid_blocks)) {
210862306a36Sopenharmony_ci		kfree(edid);
210962306a36Sopenharmony_ci		return NULL;
211062306a36Sopenharmony_ci	}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	edid->extensions = valid_blocks - 1;
211362306a36Sopenharmony_ci	edid->checksum = edid_block_compute_checksum(edid);
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	*alloc_size = edid_size_by_blocks(valid_blocks);
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	new = krealloc(edid, *alloc_size, GFP_KERNEL);
211862306a36Sopenharmony_ci	if (!new)
211962306a36Sopenharmony_ci		kfree(edid);
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	return new;
212262306a36Sopenharmony_ci}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci#define DDC_SEGMENT_ADDR 0x30
212562306a36Sopenharmony_ci/**
212662306a36Sopenharmony_ci * drm_do_probe_ddc_edid() - get EDID information via I2C
212762306a36Sopenharmony_ci * @data: I2C device adapter
212862306a36Sopenharmony_ci * @buf: EDID data buffer to be filled
212962306a36Sopenharmony_ci * @block: 128 byte EDID block to start fetching from
213062306a36Sopenharmony_ci * @len: EDID data buffer length to fetch
213162306a36Sopenharmony_ci *
213262306a36Sopenharmony_ci * Try to fetch EDID information by calling I2C driver functions.
213362306a36Sopenharmony_ci *
213462306a36Sopenharmony_ci * Return: 0 on success or -1 on failure.
213562306a36Sopenharmony_ci */
213662306a36Sopenharmony_cistatic int
213762306a36Sopenharmony_cidrm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
213862306a36Sopenharmony_ci{
213962306a36Sopenharmony_ci	struct i2c_adapter *adapter = data;
214062306a36Sopenharmony_ci	unsigned char start = block * EDID_LENGTH;
214162306a36Sopenharmony_ci	unsigned char segment = block >> 1;
214262306a36Sopenharmony_ci	unsigned char xfers = segment ? 3 : 2;
214362306a36Sopenharmony_ci	int ret, retries = 5;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	/*
214662306a36Sopenharmony_ci	 * The core I2C driver will automatically retry the transfer if the
214762306a36Sopenharmony_ci	 * adapter reports EAGAIN. However, we find that bit-banging transfers
214862306a36Sopenharmony_ci	 * are susceptible to errors under a heavily loaded machine and
214962306a36Sopenharmony_ci	 * generate spurious NAKs and timeouts. Retrying the transfer
215062306a36Sopenharmony_ci	 * of the individual block a few times seems to overcome this.
215162306a36Sopenharmony_ci	 */
215262306a36Sopenharmony_ci	do {
215362306a36Sopenharmony_ci		struct i2c_msg msgs[] = {
215462306a36Sopenharmony_ci			{
215562306a36Sopenharmony_ci				.addr	= DDC_SEGMENT_ADDR,
215662306a36Sopenharmony_ci				.flags	= 0,
215762306a36Sopenharmony_ci				.len	= 1,
215862306a36Sopenharmony_ci				.buf	= &segment,
215962306a36Sopenharmony_ci			}, {
216062306a36Sopenharmony_ci				.addr	= DDC_ADDR,
216162306a36Sopenharmony_ci				.flags	= 0,
216262306a36Sopenharmony_ci				.len	= 1,
216362306a36Sopenharmony_ci				.buf	= &start,
216462306a36Sopenharmony_ci			}, {
216562306a36Sopenharmony_ci				.addr	= DDC_ADDR,
216662306a36Sopenharmony_ci				.flags	= I2C_M_RD,
216762306a36Sopenharmony_ci				.len	= len,
216862306a36Sopenharmony_ci				.buf	= buf,
216962306a36Sopenharmony_ci			}
217062306a36Sopenharmony_ci		};
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci		/*
217362306a36Sopenharmony_ci		 * Avoid sending the segment addr to not upset non-compliant
217462306a36Sopenharmony_ci		 * DDC monitors.
217562306a36Sopenharmony_ci		 */
217662306a36Sopenharmony_ci		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci		if (ret == -ENXIO) {
217962306a36Sopenharmony_ci			DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
218062306a36Sopenharmony_ci					adapter->name);
218162306a36Sopenharmony_ci			break;
218262306a36Sopenharmony_ci		}
218362306a36Sopenharmony_ci	} while (ret != xfers && --retries);
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	return ret == xfers ? 0 : -1;
218662306a36Sopenharmony_ci}
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_cistatic void connector_bad_edid(struct drm_connector *connector,
218962306a36Sopenharmony_ci			       const struct edid *edid, int num_blocks)
219062306a36Sopenharmony_ci{
219162306a36Sopenharmony_ci	int i;
219262306a36Sopenharmony_ci	u8 last_block;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	/*
219562306a36Sopenharmony_ci	 * 0x7e in the EDID is the number of extension blocks. The EDID
219662306a36Sopenharmony_ci	 * is 1 (base block) + num_ext_blocks big. That means we can think
219762306a36Sopenharmony_ci	 * of 0x7e in the EDID of the _index_ of the last block in the
219862306a36Sopenharmony_ci	 * combined chunk of memory.
219962306a36Sopenharmony_ci	 */
220062306a36Sopenharmony_ci	last_block = edid->extensions;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	/* Calculate real checksum for the last edid extension block data */
220362306a36Sopenharmony_ci	if (last_block < num_blocks)
220462306a36Sopenharmony_ci		connector->real_edid_checksum =
220562306a36Sopenharmony_ci			edid_block_compute_checksum(edid + last_block);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
220862306a36Sopenharmony_ci		return;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID is invalid:\n",
221162306a36Sopenharmony_ci		    connector->base.id, connector->name);
221262306a36Sopenharmony_ci	for (i = 0; i < num_blocks; i++)
221362306a36Sopenharmony_ci		edid_block_dump(KERN_DEBUG, edid + i, i);
221462306a36Sopenharmony_ci}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci/* Get override or firmware EDID */
221762306a36Sopenharmony_cistatic const struct drm_edid *drm_edid_override_get(struct drm_connector *connector)
221862306a36Sopenharmony_ci{
221962306a36Sopenharmony_ci	const struct drm_edid *override = NULL;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	mutex_lock(&connector->edid_override_mutex);
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	if (connector->edid_override)
222462306a36Sopenharmony_ci		override = drm_edid_dup(connector->edid_override);
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	mutex_unlock(&connector->edid_override_mutex);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	if (!override)
222962306a36Sopenharmony_ci		override = drm_edid_load_firmware(connector);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	return IS_ERR(override) ? NULL : override;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci/* For debugfs edid_override implementation */
223562306a36Sopenharmony_ciint drm_edid_override_show(struct drm_connector *connector, struct seq_file *m)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	mutex_lock(&connector->edid_override_mutex);
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	drm_edid = connector->edid_override;
224262306a36Sopenharmony_ci	if (drm_edid)
224362306a36Sopenharmony_ci		seq_write(m, drm_edid->edid, drm_edid->size);
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	mutex_unlock(&connector->edid_override_mutex);
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return 0;
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci/* For debugfs edid_override implementation */
225162306a36Sopenharmony_ciint drm_edid_override_set(struct drm_connector *connector, const void *edid,
225262306a36Sopenharmony_ci			  size_t size)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	drm_edid = drm_edid_alloc(edid, size);
225762306a36Sopenharmony_ci	if (!drm_edid_valid(drm_edid)) {
225862306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override invalid\n",
225962306a36Sopenharmony_ci			    connector->base.id, connector->name);
226062306a36Sopenharmony_ci		drm_edid_free(drm_edid);
226162306a36Sopenharmony_ci		return -EINVAL;
226262306a36Sopenharmony_ci	}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override set\n",
226562306a36Sopenharmony_ci		    connector->base.id, connector->name);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	mutex_lock(&connector->edid_override_mutex);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	drm_edid_free(connector->edid_override);
227062306a36Sopenharmony_ci	connector->edid_override = drm_edid;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	mutex_unlock(&connector->edid_override_mutex);
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	return 0;
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci/* For debugfs edid_override implementation */
227862306a36Sopenharmony_ciint drm_edid_override_reset(struct drm_connector *connector)
227962306a36Sopenharmony_ci{
228062306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] EDID override reset\n",
228162306a36Sopenharmony_ci		    connector->base.id, connector->name);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	mutex_lock(&connector->edid_override_mutex);
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	drm_edid_free(connector->edid_override);
228662306a36Sopenharmony_ci	connector->edid_override = NULL;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	mutex_unlock(&connector->edid_override_mutex);
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	return 0;
229162306a36Sopenharmony_ci}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci/**
229462306a36Sopenharmony_ci * drm_edid_override_connector_update - add modes from override/firmware EDID
229562306a36Sopenharmony_ci * @connector: connector we're probing
229662306a36Sopenharmony_ci *
229762306a36Sopenharmony_ci * Add modes from the override/firmware EDID, if available. Only to be used from
229862306a36Sopenharmony_ci * drm_helper_probe_single_connector_modes() as a fallback for when DDC probe
229962306a36Sopenharmony_ci * failed during drm_get_edid() and caused the override/firmware EDID to be
230062306a36Sopenharmony_ci * skipped.
230162306a36Sopenharmony_ci *
230262306a36Sopenharmony_ci * Return: The number of modes added or 0 if we couldn't find any.
230362306a36Sopenharmony_ci */
230462306a36Sopenharmony_ciint drm_edid_override_connector_update(struct drm_connector *connector)
230562306a36Sopenharmony_ci{
230662306a36Sopenharmony_ci	const struct drm_edid *override;
230762306a36Sopenharmony_ci	int num_modes = 0;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	override = drm_edid_override_get(connector);
231062306a36Sopenharmony_ci	if (override) {
231162306a36Sopenharmony_ci		if (drm_edid_connector_update(connector, override) == 0)
231262306a36Sopenharmony_ci			num_modes = drm_edid_connector_add_modes(connector);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci		drm_edid_free(override);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci		drm_dbg_kms(connector->dev,
231762306a36Sopenharmony_ci			    "[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n",
231862306a36Sopenharmony_ci			    connector->base.id, connector->name, num_modes);
231962306a36Sopenharmony_ci	}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	return num_modes;
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_override_connector_update);
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_citypedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len);
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_cistatic enum edid_block_status edid_block_read(void *block, unsigned int block_num,
232862306a36Sopenharmony_ci					      read_block_fn read_block,
232962306a36Sopenharmony_ci					      void *context)
233062306a36Sopenharmony_ci{
233162306a36Sopenharmony_ci	enum edid_block_status status;
233262306a36Sopenharmony_ci	bool is_base_block = block_num == 0;
233362306a36Sopenharmony_ci	int try;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	for (try = 0; try < 4; try++) {
233662306a36Sopenharmony_ci		if (read_block(context, block, block_num, EDID_LENGTH))
233762306a36Sopenharmony_ci			return EDID_BLOCK_READ_FAIL;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci		status = edid_block_check(block, is_base_block);
234062306a36Sopenharmony_ci		if (status == EDID_BLOCK_HEADER_REPAIR) {
234162306a36Sopenharmony_ci			edid_header_fix(block);
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci			/* Retry with fixed header, update status if that worked. */
234462306a36Sopenharmony_ci			status = edid_block_check(block, is_base_block);
234562306a36Sopenharmony_ci			if (status == EDID_BLOCK_OK)
234662306a36Sopenharmony_ci				status = EDID_BLOCK_HEADER_FIXED;
234762306a36Sopenharmony_ci		}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci		if (edid_block_status_valid(status, edid_block_tag(block)))
235062306a36Sopenharmony_ci			break;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci		/* Fail early for unrepairable base block all zeros. */
235362306a36Sopenharmony_ci		if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO)
235462306a36Sopenharmony_ci			break;
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	return status;
235862306a36Sopenharmony_ci}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_cistatic struct edid *_drm_do_get_edid(struct drm_connector *connector,
236162306a36Sopenharmony_ci				     read_block_fn read_block, void *context,
236262306a36Sopenharmony_ci				     size_t *size)
236362306a36Sopenharmony_ci{
236462306a36Sopenharmony_ci	enum edid_block_status status;
236562306a36Sopenharmony_ci	int i, num_blocks, invalid_blocks = 0;
236662306a36Sopenharmony_ci	const struct drm_edid *override;
236762306a36Sopenharmony_ci	struct edid *edid, *new;
236862306a36Sopenharmony_ci	size_t alloc_size = EDID_LENGTH;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	override = drm_edid_override_get(connector);
237162306a36Sopenharmony_ci	if (override) {
237262306a36Sopenharmony_ci		alloc_size = override->size;
237362306a36Sopenharmony_ci		edid = kmemdup(override->edid, alloc_size, GFP_KERNEL);
237462306a36Sopenharmony_ci		drm_edid_free(override);
237562306a36Sopenharmony_ci		if (!edid)
237662306a36Sopenharmony_ci			return NULL;
237762306a36Sopenharmony_ci		goto ok;
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	edid = kmalloc(alloc_size, GFP_KERNEL);
238162306a36Sopenharmony_ci	if (!edid)
238262306a36Sopenharmony_ci		return NULL;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	status = edid_block_read(edid, 0, read_block, context);
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	edid_block_status_print(status, edid, 0);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	if (status == EDID_BLOCK_READ_FAIL)
238962306a36Sopenharmony_ci		goto fail;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	/* FIXME: Clarify what a corrupt EDID actually means. */
239262306a36Sopenharmony_ci	if (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION)
239362306a36Sopenharmony_ci		connector->edid_corrupt = false;
239462306a36Sopenharmony_ci	else
239562306a36Sopenharmony_ci		connector->edid_corrupt = true;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	if (!edid_block_status_valid(status, edid_block_tag(edid))) {
239862306a36Sopenharmony_ci		if (status == EDID_BLOCK_ZERO)
239962306a36Sopenharmony_ci			connector->null_edid_counter++;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci		connector_bad_edid(connector, edid, 1);
240262306a36Sopenharmony_ci		goto fail;
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	if (!edid_extension_block_count(edid))
240662306a36Sopenharmony_ci		goto ok;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	alloc_size = edid_size(edid);
240962306a36Sopenharmony_ci	new = krealloc(edid, alloc_size, GFP_KERNEL);
241062306a36Sopenharmony_ci	if (!new)
241162306a36Sopenharmony_ci		goto fail;
241262306a36Sopenharmony_ci	edid = new;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	num_blocks = edid_block_count(edid);
241562306a36Sopenharmony_ci	for (i = 1; i < num_blocks; i++) {
241662306a36Sopenharmony_ci		void *block = (void *)edid_block_data(edid, i);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci		status = edid_block_read(block, i, read_block, context);
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci		edid_block_status_print(status, block, i);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci		if (!edid_block_status_valid(status, edid_block_tag(block))) {
242362306a36Sopenharmony_ci			if (status == EDID_BLOCK_READ_FAIL)
242462306a36Sopenharmony_ci				goto fail;
242562306a36Sopenharmony_ci			invalid_blocks++;
242662306a36Sopenharmony_ci		} else if (i == 1) {
242762306a36Sopenharmony_ci			/*
242862306a36Sopenharmony_ci			 * If the first EDID extension is a CTA extension, and
242962306a36Sopenharmony_ci			 * the first Data Block is HF-EEODB, override the
243062306a36Sopenharmony_ci			 * extension block count.
243162306a36Sopenharmony_ci			 *
243262306a36Sopenharmony_ci			 * Note: HF-EEODB could specify a smaller extension
243362306a36Sopenharmony_ci			 * count too, but we can't risk allocating a smaller
243462306a36Sopenharmony_ci			 * amount.
243562306a36Sopenharmony_ci			 */
243662306a36Sopenharmony_ci			int eeodb = edid_hfeeodb_block_count(edid);
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci			if (eeodb > num_blocks) {
243962306a36Sopenharmony_ci				num_blocks = eeodb;
244062306a36Sopenharmony_ci				alloc_size = edid_size_by_blocks(num_blocks);
244162306a36Sopenharmony_ci				new = krealloc(edid, alloc_size, GFP_KERNEL);
244262306a36Sopenharmony_ci				if (!new)
244362306a36Sopenharmony_ci					goto fail;
244462306a36Sopenharmony_ci				edid = new;
244562306a36Sopenharmony_ci			}
244662306a36Sopenharmony_ci		}
244762306a36Sopenharmony_ci	}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	if (invalid_blocks) {
245062306a36Sopenharmony_ci		connector_bad_edid(connector, edid, num_blocks);
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		edid = edid_filter_invalid_blocks(edid, &alloc_size);
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ciok:
245662306a36Sopenharmony_ci	if (size)
245762306a36Sopenharmony_ci		*size = alloc_size;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	return edid;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_cifail:
246262306a36Sopenharmony_ci	kfree(edid);
246362306a36Sopenharmony_ci	return NULL;
246462306a36Sopenharmony_ci}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci/**
246762306a36Sopenharmony_ci * drm_do_get_edid - get EDID data using a custom EDID block read function
246862306a36Sopenharmony_ci * @connector: connector we're probing
246962306a36Sopenharmony_ci * @read_block: EDID block read function
247062306a36Sopenharmony_ci * @context: private data passed to the block read function
247162306a36Sopenharmony_ci *
247262306a36Sopenharmony_ci * When the I2C adapter connected to the DDC bus is hidden behind a device that
247362306a36Sopenharmony_ci * exposes a different interface to read EDID blocks this function can be used
247462306a36Sopenharmony_ci * to get EDID data using a custom block read function.
247562306a36Sopenharmony_ci *
247662306a36Sopenharmony_ci * As in the general case the DDC bus is accessible by the kernel at the I2C
247762306a36Sopenharmony_ci * level, drivers must make all reasonable efforts to expose it as an I2C
247862306a36Sopenharmony_ci * adapter and use drm_get_edid() instead of abusing this function.
247962306a36Sopenharmony_ci *
248062306a36Sopenharmony_ci * The EDID may be overridden using debugfs override_edid or firmware EDID
248162306a36Sopenharmony_ci * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
248262306a36Sopenharmony_ci * order. Having either of them bypasses actual EDID reads.
248362306a36Sopenharmony_ci *
248462306a36Sopenharmony_ci * Return: Pointer to valid EDID or NULL if we couldn't find any.
248562306a36Sopenharmony_ci */
248662306a36Sopenharmony_cistruct edid *drm_do_get_edid(struct drm_connector *connector,
248762306a36Sopenharmony_ci			     read_block_fn read_block,
248862306a36Sopenharmony_ci			     void *context)
248962306a36Sopenharmony_ci{
249062306a36Sopenharmony_ci	return _drm_do_get_edid(connector, read_block, context, NULL);
249162306a36Sopenharmony_ci}
249262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_do_get_edid);
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci/**
249562306a36Sopenharmony_ci * drm_edid_raw - Get a pointer to the raw EDID data.
249662306a36Sopenharmony_ci * @drm_edid: drm_edid container
249762306a36Sopenharmony_ci *
249862306a36Sopenharmony_ci * Get a pointer to the raw EDID data.
249962306a36Sopenharmony_ci *
250062306a36Sopenharmony_ci * This is for transition only. Avoid using this like the plague.
250162306a36Sopenharmony_ci *
250262306a36Sopenharmony_ci * Return: Pointer to raw EDID data.
250362306a36Sopenharmony_ci */
250462306a36Sopenharmony_ciconst struct edid *drm_edid_raw(const struct drm_edid *drm_edid)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	if (!drm_edid || !drm_edid->size)
250762306a36Sopenharmony_ci		return NULL;
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	/*
251062306a36Sopenharmony_ci	 * Do not return pointers where relying on EDID extension count would
251162306a36Sopenharmony_ci	 * lead to buffer overflow.
251262306a36Sopenharmony_ci	 */
251362306a36Sopenharmony_ci	if (WARN_ON(edid_size(drm_edid->edid) > drm_edid->size))
251462306a36Sopenharmony_ci		return NULL;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	return drm_edid->edid;
251762306a36Sopenharmony_ci}
251862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_raw);
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci/* Allocate struct drm_edid container *without* duplicating the edid data */
252162306a36Sopenharmony_cistatic const struct drm_edid *_drm_edid_alloc(const void *edid, size_t size)
252262306a36Sopenharmony_ci{
252362306a36Sopenharmony_ci	struct drm_edid *drm_edid;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	if (!edid || !size || size < EDID_LENGTH)
252662306a36Sopenharmony_ci		return NULL;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	drm_edid = kzalloc(sizeof(*drm_edid), GFP_KERNEL);
252962306a36Sopenharmony_ci	if (drm_edid) {
253062306a36Sopenharmony_ci		drm_edid->edid = edid;
253162306a36Sopenharmony_ci		drm_edid->size = size;
253262306a36Sopenharmony_ci	}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	return drm_edid;
253562306a36Sopenharmony_ci}
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci/**
253862306a36Sopenharmony_ci * drm_edid_alloc - Allocate a new drm_edid container
253962306a36Sopenharmony_ci * @edid: Pointer to raw EDID data
254062306a36Sopenharmony_ci * @size: Size of memory allocated for EDID
254162306a36Sopenharmony_ci *
254262306a36Sopenharmony_ci * Allocate a new drm_edid container. Do not calculate edid size from edid, pass
254362306a36Sopenharmony_ci * the actual size that has been allocated for the data. There is no validation
254462306a36Sopenharmony_ci * of the raw EDID data against the size, but at least the EDID base block must
254562306a36Sopenharmony_ci * fit in the buffer.
254662306a36Sopenharmony_ci *
254762306a36Sopenharmony_ci * The returned pointer must be freed using drm_edid_free().
254862306a36Sopenharmony_ci *
254962306a36Sopenharmony_ci * Return: drm_edid container, or NULL on errors
255062306a36Sopenharmony_ci */
255162306a36Sopenharmony_ciconst struct drm_edid *drm_edid_alloc(const void *edid, size_t size)
255262306a36Sopenharmony_ci{
255362306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	if (!edid || !size || size < EDID_LENGTH)
255662306a36Sopenharmony_ci		return NULL;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	edid = kmemdup(edid, size, GFP_KERNEL);
255962306a36Sopenharmony_ci	if (!edid)
256062306a36Sopenharmony_ci		return NULL;
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	drm_edid = _drm_edid_alloc(edid, size);
256362306a36Sopenharmony_ci	if (!drm_edid)
256462306a36Sopenharmony_ci		kfree(edid);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	return drm_edid;
256762306a36Sopenharmony_ci}
256862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_alloc);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci/**
257162306a36Sopenharmony_ci * drm_edid_dup - Duplicate a drm_edid container
257262306a36Sopenharmony_ci * @drm_edid: EDID to duplicate
257362306a36Sopenharmony_ci *
257462306a36Sopenharmony_ci * The returned pointer must be freed using drm_edid_free().
257562306a36Sopenharmony_ci *
257662306a36Sopenharmony_ci * Returns: drm_edid container copy, or NULL on errors
257762306a36Sopenharmony_ci */
257862306a36Sopenharmony_ciconst struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid)
257962306a36Sopenharmony_ci{
258062306a36Sopenharmony_ci	if (!drm_edid)
258162306a36Sopenharmony_ci		return NULL;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	return drm_edid_alloc(drm_edid->edid, drm_edid->size);
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_dup);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci/**
258862306a36Sopenharmony_ci * drm_edid_free - Free the drm_edid container
258962306a36Sopenharmony_ci * @drm_edid: EDID to free
259062306a36Sopenharmony_ci */
259162306a36Sopenharmony_civoid drm_edid_free(const struct drm_edid *drm_edid)
259262306a36Sopenharmony_ci{
259362306a36Sopenharmony_ci	if (!drm_edid)
259462306a36Sopenharmony_ci		return;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	kfree(drm_edid->edid);
259762306a36Sopenharmony_ci	kfree(drm_edid);
259862306a36Sopenharmony_ci}
259962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_free);
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci/**
260262306a36Sopenharmony_ci * drm_probe_ddc() - probe DDC presence
260362306a36Sopenharmony_ci * @adapter: I2C adapter to probe
260462306a36Sopenharmony_ci *
260562306a36Sopenharmony_ci * Return: True on success, false on failure.
260662306a36Sopenharmony_ci */
260762306a36Sopenharmony_cibool
260862306a36Sopenharmony_cidrm_probe_ddc(struct i2c_adapter *adapter)
260962306a36Sopenharmony_ci{
261062306a36Sopenharmony_ci	unsigned char out;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0);
261362306a36Sopenharmony_ci}
261462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_probe_ddc);
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci/**
261762306a36Sopenharmony_ci * drm_get_edid - get EDID data, if available
261862306a36Sopenharmony_ci * @connector: connector we're probing
261962306a36Sopenharmony_ci * @adapter: I2C adapter to use for DDC
262062306a36Sopenharmony_ci *
262162306a36Sopenharmony_ci * Poke the given I2C channel to grab EDID data if possible.  If found,
262262306a36Sopenharmony_ci * attach it to the connector.
262362306a36Sopenharmony_ci *
262462306a36Sopenharmony_ci * Return: Pointer to valid EDID or NULL if we couldn't find any.
262562306a36Sopenharmony_ci */
262662306a36Sopenharmony_cistruct edid *drm_get_edid(struct drm_connector *connector,
262762306a36Sopenharmony_ci			  struct i2c_adapter *adapter)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	struct edid *edid;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	if (connector->force == DRM_FORCE_OFF)
263262306a36Sopenharmony_ci		return NULL;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
263562306a36Sopenharmony_ci		return NULL;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);
263862306a36Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
263962306a36Sopenharmony_ci	return edid;
264062306a36Sopenharmony_ci}
264162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_get_edid);
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci/**
264462306a36Sopenharmony_ci * drm_edid_read_custom - Read EDID data using given EDID block read function
264562306a36Sopenharmony_ci * @connector: Connector to use
264662306a36Sopenharmony_ci * @read_block: EDID block read function
264762306a36Sopenharmony_ci * @context: Private data passed to the block read function
264862306a36Sopenharmony_ci *
264962306a36Sopenharmony_ci * When the I2C adapter connected to the DDC bus is hidden behind a device that
265062306a36Sopenharmony_ci * exposes a different interface to read EDID blocks this function can be used
265162306a36Sopenharmony_ci * to get EDID data using a custom block read function.
265262306a36Sopenharmony_ci *
265362306a36Sopenharmony_ci * As in the general case the DDC bus is accessible by the kernel at the I2C
265462306a36Sopenharmony_ci * level, drivers must make all reasonable efforts to expose it as an I2C
265562306a36Sopenharmony_ci * adapter and use drm_edid_read() or drm_edid_read_ddc() instead of abusing
265662306a36Sopenharmony_ci * this function.
265762306a36Sopenharmony_ci *
265862306a36Sopenharmony_ci * The EDID may be overridden using debugfs override_edid or firmware EDID
265962306a36Sopenharmony_ci * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
266062306a36Sopenharmony_ci * order. Having either of them bypasses actual EDID reads.
266162306a36Sopenharmony_ci *
266262306a36Sopenharmony_ci * The returned pointer must be freed using drm_edid_free().
266362306a36Sopenharmony_ci *
266462306a36Sopenharmony_ci * Return: Pointer to EDID, or NULL if probe/read failed.
266562306a36Sopenharmony_ci */
266662306a36Sopenharmony_ciconst struct drm_edid *drm_edid_read_custom(struct drm_connector *connector,
266762306a36Sopenharmony_ci					    read_block_fn read_block,
266862306a36Sopenharmony_ci					    void *context)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
267162306a36Sopenharmony_ci	struct edid *edid;
267262306a36Sopenharmony_ci	size_t size = 0;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	edid = _drm_do_get_edid(connector, read_block, context, &size);
267562306a36Sopenharmony_ci	if (!edid)
267662306a36Sopenharmony_ci		return NULL;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* Sanity check for now */
267962306a36Sopenharmony_ci	drm_WARN_ON(connector->dev, !size);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	drm_edid = _drm_edid_alloc(edid, size);
268262306a36Sopenharmony_ci	if (!drm_edid)
268362306a36Sopenharmony_ci		kfree(edid);
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	return drm_edid;
268662306a36Sopenharmony_ci}
268762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_read_custom);
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci/**
269062306a36Sopenharmony_ci * drm_edid_read_ddc - Read EDID data using given I2C adapter
269162306a36Sopenharmony_ci * @connector: Connector to use
269262306a36Sopenharmony_ci * @adapter: I2C adapter to use for DDC
269362306a36Sopenharmony_ci *
269462306a36Sopenharmony_ci * Read EDID using the given I2C adapter.
269562306a36Sopenharmony_ci *
269662306a36Sopenharmony_ci * The EDID may be overridden using debugfs override_edid or firmware EDID
269762306a36Sopenharmony_ci * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
269862306a36Sopenharmony_ci * order. Having either of them bypasses actual EDID reads.
269962306a36Sopenharmony_ci *
270062306a36Sopenharmony_ci * Prefer initializing connector->ddc with drm_connector_init_with_ddc() and
270162306a36Sopenharmony_ci * using drm_edid_read() instead of this function.
270262306a36Sopenharmony_ci *
270362306a36Sopenharmony_ci * The returned pointer must be freed using drm_edid_free().
270462306a36Sopenharmony_ci *
270562306a36Sopenharmony_ci * Return: Pointer to EDID, or NULL if probe/read failed.
270662306a36Sopenharmony_ci */
270762306a36Sopenharmony_ciconst struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
270862306a36Sopenharmony_ci					 struct i2c_adapter *adapter)
270962306a36Sopenharmony_ci{
271062306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (connector->force == DRM_FORCE_OFF)
271362306a36Sopenharmony_ci		return NULL;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
271662306a36Sopenharmony_ci		return NULL;
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	drm_edid = drm_edid_read_custom(connector, drm_do_probe_ddc_edid, adapter);
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	/* Note: Do *not* call connector updates here. */
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	return drm_edid;
272362306a36Sopenharmony_ci}
272462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_read_ddc);
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci/**
272762306a36Sopenharmony_ci * drm_edid_read - Read EDID data using connector's I2C adapter
272862306a36Sopenharmony_ci * @connector: Connector to use
272962306a36Sopenharmony_ci *
273062306a36Sopenharmony_ci * Read EDID using the connector's I2C adapter.
273162306a36Sopenharmony_ci *
273262306a36Sopenharmony_ci * The EDID may be overridden using debugfs override_edid or firmware EDID
273362306a36Sopenharmony_ci * (drm_edid_load_firmware() and drm.edid_firmware parameter), in this priority
273462306a36Sopenharmony_ci * order. Having either of them bypasses actual EDID reads.
273562306a36Sopenharmony_ci *
273662306a36Sopenharmony_ci * The returned pointer must be freed using drm_edid_free().
273762306a36Sopenharmony_ci *
273862306a36Sopenharmony_ci * Return: Pointer to EDID, or NULL if probe/read failed.
273962306a36Sopenharmony_ci */
274062306a36Sopenharmony_ciconst struct drm_edid *drm_edid_read(struct drm_connector *connector)
274162306a36Sopenharmony_ci{
274262306a36Sopenharmony_ci	if (drm_WARN_ON(connector->dev, !connector->ddc))
274362306a36Sopenharmony_ci		return NULL;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	return drm_edid_read_ddc(connector, connector->ddc);
274662306a36Sopenharmony_ci}
274762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_read);
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_cistatic u32 edid_extract_panel_id(const struct edid *edid)
275062306a36Sopenharmony_ci{
275162306a36Sopenharmony_ci	/*
275262306a36Sopenharmony_ci	 * We represent the ID as a 32-bit number so it can easily be compared
275362306a36Sopenharmony_ci	 * with "==".
275462306a36Sopenharmony_ci	 *
275562306a36Sopenharmony_ci	 * NOTE that we deal with endianness differently for the top half
275662306a36Sopenharmony_ci	 * of this ID than for the bottom half. The bottom half (the product
275762306a36Sopenharmony_ci	 * id) gets decoded as little endian by the EDID_PRODUCT_ID because
275862306a36Sopenharmony_ci	 * that's how everyone seems to interpret it. The top half (the mfg_id)
275962306a36Sopenharmony_ci	 * gets stored as big endian because that makes
276062306a36Sopenharmony_ci	 * drm_edid_encode_panel_id() and drm_edid_decode_panel_id() easier
276162306a36Sopenharmony_ci	 * to write (it's easier to extract the ASCII). It doesn't really
276262306a36Sopenharmony_ci	 * matter, though, as long as the number here is unique.
276362306a36Sopenharmony_ci	 */
276462306a36Sopenharmony_ci	return (u32)edid->mfg_id[0] << 24   |
276562306a36Sopenharmony_ci	       (u32)edid->mfg_id[1] << 16   |
276662306a36Sopenharmony_ci	       (u32)EDID_PRODUCT_ID(edid);
276762306a36Sopenharmony_ci}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci/**
277062306a36Sopenharmony_ci * drm_edid_get_panel_id - Get a panel's ID through DDC
277162306a36Sopenharmony_ci * @adapter: I2C adapter to use for DDC
277262306a36Sopenharmony_ci *
277362306a36Sopenharmony_ci * This function reads the first block of the EDID of a panel and (assuming
277462306a36Sopenharmony_ci * that the EDID is valid) extracts the ID out of it. The ID is a 32-bit value
277562306a36Sopenharmony_ci * (16 bits of manufacturer ID and 16 bits of per-manufacturer ID) that's
277662306a36Sopenharmony_ci * supposed to be different for each different modem of panel.
277762306a36Sopenharmony_ci *
277862306a36Sopenharmony_ci * This function is intended to be used during early probing on devices where
277962306a36Sopenharmony_ci * more than one panel might be present. Because of its intended use it must
278062306a36Sopenharmony_ci * assume that the EDID of the panel is correct, at least as far as the ID
278162306a36Sopenharmony_ci * is concerned (in other words, we don't process any overrides here).
278262306a36Sopenharmony_ci *
278362306a36Sopenharmony_ci * NOTE: it's expected that this function and drm_do_get_edid() will both
278462306a36Sopenharmony_ci * be read the EDID, but there is no caching between them. Since we're only
278562306a36Sopenharmony_ci * reading the first block, hopefully this extra overhead won't be too big.
278662306a36Sopenharmony_ci *
278762306a36Sopenharmony_ci * Return: A 32-bit ID that should be different for each make/model of panel.
278862306a36Sopenharmony_ci *         See the functions drm_edid_encode_panel_id() and
278962306a36Sopenharmony_ci *         drm_edid_decode_panel_id() for some details on the structure of this
279062306a36Sopenharmony_ci *         ID.
279162306a36Sopenharmony_ci */
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ciu32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
279462306a36Sopenharmony_ci{
279562306a36Sopenharmony_ci	enum edid_block_status status;
279662306a36Sopenharmony_ci	void *base_block;
279762306a36Sopenharmony_ci	u32 panel_id = 0;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	/*
280062306a36Sopenharmony_ci	 * There are no manufacturer IDs of 0, so if there is a problem reading
280162306a36Sopenharmony_ci	 * the EDID then we'll just return 0.
280262306a36Sopenharmony_ci	 */
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	base_block = kzalloc(EDID_LENGTH, GFP_KERNEL);
280562306a36Sopenharmony_ci	if (!base_block)
280662306a36Sopenharmony_ci		return 0;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter);
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	edid_block_status_print(status, base_block, 0);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	if (edid_block_status_valid(status, edid_block_tag(base_block)))
281362306a36Sopenharmony_ci		panel_id = edid_extract_panel_id(base_block);
281462306a36Sopenharmony_ci	else
281562306a36Sopenharmony_ci		edid_block_dump(KERN_NOTICE, base_block, 0);
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	kfree(base_block);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	return panel_id;
282062306a36Sopenharmony_ci}
282162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_get_panel_id);
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci/**
282462306a36Sopenharmony_ci * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output
282562306a36Sopenharmony_ci * @connector: connector we're probing
282662306a36Sopenharmony_ci * @adapter: I2C adapter to use for DDC
282762306a36Sopenharmony_ci *
282862306a36Sopenharmony_ci * Wrapper around drm_get_edid() for laptops with dual GPUs using one set of
282962306a36Sopenharmony_ci * outputs. The wrapper adds the requisite vga_switcheroo calls to temporarily
283062306a36Sopenharmony_ci * switch DDC to the GPU which is retrieving EDID.
283162306a36Sopenharmony_ci *
283262306a36Sopenharmony_ci * Return: Pointer to valid EDID or %NULL if we couldn't find any.
283362306a36Sopenharmony_ci */
283462306a36Sopenharmony_cistruct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
283562306a36Sopenharmony_ci				     struct i2c_adapter *adapter)
283662306a36Sopenharmony_ci{
283762306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
283862306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev->dev);
283962306a36Sopenharmony_ci	struct edid *edid;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, !dev_is_pci(dev->dev)))
284262306a36Sopenharmony_ci		return NULL;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	vga_switcheroo_lock_ddc(pdev);
284562306a36Sopenharmony_ci	edid = drm_get_edid(connector, adapter);
284662306a36Sopenharmony_ci	vga_switcheroo_unlock_ddc(pdev);
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	return edid;
284962306a36Sopenharmony_ci}
285062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_get_edid_switcheroo);
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci/**
285362306a36Sopenharmony_ci * drm_edid_read_switcheroo - get EDID data for a vga_switcheroo output
285462306a36Sopenharmony_ci * @connector: connector we're probing
285562306a36Sopenharmony_ci * @adapter: I2C adapter to use for DDC
285662306a36Sopenharmony_ci *
285762306a36Sopenharmony_ci * Wrapper around drm_edid_read_ddc() for laptops with dual GPUs using one set
285862306a36Sopenharmony_ci * of outputs. The wrapper adds the requisite vga_switcheroo calls to
285962306a36Sopenharmony_ci * temporarily switch DDC to the GPU which is retrieving EDID.
286062306a36Sopenharmony_ci *
286162306a36Sopenharmony_ci * Return: Pointer to valid EDID or %NULL if we couldn't find any.
286262306a36Sopenharmony_ci */
286362306a36Sopenharmony_ciconst struct drm_edid *drm_edid_read_switcheroo(struct drm_connector *connector,
286462306a36Sopenharmony_ci						struct i2c_adapter *adapter)
286562306a36Sopenharmony_ci{
286662306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
286762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev->dev);
286862306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, !dev_is_pci(dev->dev)))
287162306a36Sopenharmony_ci		return NULL;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	vga_switcheroo_lock_ddc(pdev);
287462306a36Sopenharmony_ci	drm_edid = drm_edid_read_ddc(connector, adapter);
287562306a36Sopenharmony_ci	vga_switcheroo_unlock_ddc(pdev);
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci	return drm_edid;
287862306a36Sopenharmony_ci}
287962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_read_switcheroo);
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci/**
288262306a36Sopenharmony_ci * drm_edid_duplicate - duplicate an EDID and the extensions
288362306a36Sopenharmony_ci * @edid: EDID to duplicate
288462306a36Sopenharmony_ci *
288562306a36Sopenharmony_ci * Return: Pointer to duplicated EDID or NULL on allocation failure.
288662306a36Sopenharmony_ci */
288762306a36Sopenharmony_cistruct edid *drm_edid_duplicate(const struct edid *edid)
288862306a36Sopenharmony_ci{
288962306a36Sopenharmony_ci	if (!edid)
289062306a36Sopenharmony_ci		return NULL;
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	return kmemdup(edid, edid_size(edid), GFP_KERNEL);
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_duplicate);
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci/*** EDID parsing ***/
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci/**
289962306a36Sopenharmony_ci * edid_get_quirks - return quirk flags for a given EDID
290062306a36Sopenharmony_ci * @drm_edid: EDID to process
290162306a36Sopenharmony_ci *
290262306a36Sopenharmony_ci * This tells subsequent routines what fixes they need to apply.
290362306a36Sopenharmony_ci */
290462306a36Sopenharmony_cistatic u32 edid_get_quirks(const struct drm_edid *drm_edid)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	u32 panel_id = edid_extract_panel_id(drm_edid->edid);
290762306a36Sopenharmony_ci	const struct edid_quirk *quirk;
290862306a36Sopenharmony_ci	int i;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
291162306a36Sopenharmony_ci		quirk = &edid_quirk_list[i];
291262306a36Sopenharmony_ci		if (quirk->panel_id == panel_id)
291362306a36Sopenharmony_ci			return quirk->quirks;
291462306a36Sopenharmony_ci	}
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	return 0;
291762306a36Sopenharmony_ci}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
292062306a36Sopenharmony_ci#define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci/*
292362306a36Sopenharmony_ci * Walk the mode list for connector, clearing the preferred status on existing
292462306a36Sopenharmony_ci * modes and setting it anew for the right mode ala quirks.
292562306a36Sopenharmony_ci */
292662306a36Sopenharmony_cistatic void edid_fixup_preferred(struct drm_connector *connector)
292762306a36Sopenharmony_ci{
292862306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
292962306a36Sopenharmony_ci	struct drm_display_mode *t, *cur_mode, *preferred_mode;
293062306a36Sopenharmony_ci	int target_refresh = 0;
293162306a36Sopenharmony_ci	int cur_vrefresh, preferred_vrefresh;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	if (list_empty(&connector->probed_modes))
293462306a36Sopenharmony_ci		return;
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_PREFER_LARGE_60)
293762306a36Sopenharmony_ci		target_refresh = 60;
293862306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_PREFER_LARGE_75)
293962306a36Sopenharmony_ci		target_refresh = 75;
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	preferred_mode = list_first_entry(&connector->probed_modes,
294262306a36Sopenharmony_ci					  struct drm_display_mode, head);
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
294562306a36Sopenharmony_ci		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci		if (cur_mode == preferred_mode)
294862306a36Sopenharmony_ci			continue;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci		/* Largest mode is preferred */
295162306a36Sopenharmony_ci		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
295262306a36Sopenharmony_ci			preferred_mode = cur_mode;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci		cur_vrefresh = drm_mode_vrefresh(cur_mode);
295562306a36Sopenharmony_ci		preferred_vrefresh = drm_mode_vrefresh(preferred_mode);
295662306a36Sopenharmony_ci		/* At a given size, try to get closest to target refresh */
295762306a36Sopenharmony_ci		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
295862306a36Sopenharmony_ci		    MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) <
295962306a36Sopenharmony_ci		    MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {
296062306a36Sopenharmony_ci			preferred_mode = cur_mode;
296162306a36Sopenharmony_ci		}
296262306a36Sopenharmony_ci	}
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
296562306a36Sopenharmony_ci}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_cistatic bool
296862306a36Sopenharmony_cimode_is_rb(const struct drm_display_mode *mode)
296962306a36Sopenharmony_ci{
297062306a36Sopenharmony_ci	return (mode->htotal - mode->hdisplay == 160) &&
297162306a36Sopenharmony_ci	       (mode->hsync_end - mode->hdisplay == 80) &&
297262306a36Sopenharmony_ci	       (mode->hsync_end - mode->hsync_start == 32) &&
297362306a36Sopenharmony_ci	       (mode->vsync_start - mode->vdisplay == 3);
297462306a36Sopenharmony_ci}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci/*
297762306a36Sopenharmony_ci * drm_mode_find_dmt - Create a copy of a mode if present in DMT
297862306a36Sopenharmony_ci * @dev: Device to duplicate against
297962306a36Sopenharmony_ci * @hsize: Mode width
298062306a36Sopenharmony_ci * @vsize: Mode height
298162306a36Sopenharmony_ci * @fresh: Mode refresh rate
298262306a36Sopenharmony_ci * @rb: Mode reduced-blanking-ness
298362306a36Sopenharmony_ci *
298462306a36Sopenharmony_ci * Walk the DMT mode list looking for a match for the given parameters.
298562306a36Sopenharmony_ci *
298662306a36Sopenharmony_ci * Return: A newly allocated copy of the mode, or NULL if not found.
298762306a36Sopenharmony_ci */
298862306a36Sopenharmony_cistruct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
298962306a36Sopenharmony_ci					   int hsize, int vsize, int fresh,
299062306a36Sopenharmony_ci					   bool rb)
299162306a36Sopenharmony_ci{
299262306a36Sopenharmony_ci	int i;
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
299562306a36Sopenharmony_ci		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci		if (hsize != ptr->hdisplay)
299862306a36Sopenharmony_ci			continue;
299962306a36Sopenharmony_ci		if (vsize != ptr->vdisplay)
300062306a36Sopenharmony_ci			continue;
300162306a36Sopenharmony_ci		if (fresh != drm_mode_vrefresh(ptr))
300262306a36Sopenharmony_ci			continue;
300362306a36Sopenharmony_ci		if (rb != mode_is_rb(ptr))
300462306a36Sopenharmony_ci			continue;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci		return drm_mode_duplicate(dev, ptr);
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	return NULL;
301062306a36Sopenharmony_ci}
301162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mode_find_dmt);
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_cistatic bool is_display_descriptor(const struct detailed_timing *descriptor, u8 type)
301462306a36Sopenharmony_ci{
301562306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
301662306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.pad1) != 2);
301762306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.type) != 3);
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	return descriptor->pixel_clock == 0 &&
302062306a36Sopenharmony_ci		descriptor->data.other_data.pad1 == 0 &&
302162306a36Sopenharmony_ci		descriptor->data.other_data.type == type;
302262306a36Sopenharmony_ci}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_cistatic bool is_detailed_timing_descriptor(const struct detailed_timing *descriptor)
302562306a36Sopenharmony_ci{
302662306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), pixel_clock) != 0);
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	return descriptor->pixel_clock != 0;
302962306a36Sopenharmony_ci}
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_citypedef void detailed_cb(const struct detailed_timing *timing, void *closure);
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_cistatic void
303462306a36Sopenharmony_cicea_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
303562306a36Sopenharmony_ci{
303662306a36Sopenharmony_ci	int i, n;
303762306a36Sopenharmony_ci	u8 d = ext[0x02];
303862306a36Sopenharmony_ci	const u8 *det_base = ext + d;
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci	if (d < 4 || d > 127)
304162306a36Sopenharmony_ci		return;
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	n = (127 - d) / 18;
304462306a36Sopenharmony_ci	for (i = 0; i < n; i++)
304562306a36Sopenharmony_ci		cb((const struct detailed_timing *)(det_base + 18 * i), closure);
304662306a36Sopenharmony_ci}
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_cistatic void
304962306a36Sopenharmony_civtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
305062306a36Sopenharmony_ci{
305162306a36Sopenharmony_ci	unsigned int i, n = min((int)ext[0x02], 6);
305262306a36Sopenharmony_ci	const u8 *det_base = ext + 5;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	if (ext[0x01] != 1)
305562306a36Sopenharmony_ci		return; /* unknown version */
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	for (i = 0; i < n; i++)
305862306a36Sopenharmony_ci		cb((const struct detailed_timing *)(det_base + 18 * i), closure);
305962306a36Sopenharmony_ci}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_cistatic void drm_for_each_detailed_block(const struct drm_edid *drm_edid,
306262306a36Sopenharmony_ci					detailed_cb *cb, void *closure)
306362306a36Sopenharmony_ci{
306462306a36Sopenharmony_ci	struct drm_edid_iter edid_iter;
306562306a36Sopenharmony_ci	const u8 *ext;
306662306a36Sopenharmony_ci	int i;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	if (!drm_edid)
306962306a36Sopenharmony_ci		return;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
307262306a36Sopenharmony_ci		cb(&drm_edid->edid->detailed_timings[i], closure);
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci	drm_edid_iter_begin(drm_edid, &edid_iter);
307562306a36Sopenharmony_ci	drm_edid_iter_for_each(ext, &edid_iter) {
307662306a36Sopenharmony_ci		switch (*ext) {
307762306a36Sopenharmony_ci		case CEA_EXT:
307862306a36Sopenharmony_ci			cea_for_each_detailed_block(ext, cb, closure);
307962306a36Sopenharmony_ci			break;
308062306a36Sopenharmony_ci		case VTB_EXT:
308162306a36Sopenharmony_ci			vtb_for_each_detailed_block(ext, cb, closure);
308262306a36Sopenharmony_ci			break;
308362306a36Sopenharmony_ci		default:
308462306a36Sopenharmony_ci			break;
308562306a36Sopenharmony_ci		}
308662306a36Sopenharmony_ci	}
308762306a36Sopenharmony_ci	drm_edid_iter_end(&edid_iter);
308862306a36Sopenharmony_ci}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_cistatic void
309162306a36Sopenharmony_ciis_rb(const struct detailed_timing *descriptor, void *data)
309262306a36Sopenharmony_ci{
309362306a36Sopenharmony_ci	bool *res = data;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
309662306a36Sopenharmony_ci		return;
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
309962306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.cvt.flags) != 15);
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	if (descriptor->data.other_data.data.range.flags == DRM_EDID_CVT_SUPPORT_FLAG &&
310262306a36Sopenharmony_ci	    descriptor->data.other_data.data.range.formula.cvt.flags & DRM_EDID_CVT_FLAGS_REDUCED_BLANKING)
310362306a36Sopenharmony_ci		*res = true;
310462306a36Sopenharmony_ci}
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci/* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
310762306a36Sopenharmony_cistatic bool
310862306a36Sopenharmony_cidrm_monitor_supports_rb(const struct drm_edid *drm_edid)
310962306a36Sopenharmony_ci{
311062306a36Sopenharmony_ci	if (drm_edid->edid->revision >= 4) {
311162306a36Sopenharmony_ci		bool ret = false;
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, is_rb, &ret);
311462306a36Sopenharmony_ci		return ret;
311562306a36Sopenharmony_ci	}
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci	return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
311862306a36Sopenharmony_ci}
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_cistatic void
312162306a36Sopenharmony_cifind_gtf2(const struct detailed_timing *descriptor, void *data)
312262306a36Sopenharmony_ci{
312362306a36Sopenharmony_ci	const struct detailed_timing **res = data;
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
312662306a36Sopenharmony_ci		return;
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (descriptor->data.other_data.data.range.flags == DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG)
313162306a36Sopenharmony_ci		*res = descriptor;
313262306a36Sopenharmony_ci}
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci/* Secondary GTF curve kicks in above some break frequency */
313562306a36Sopenharmony_cistatic int
313662306a36Sopenharmony_cidrm_gtf2_hbreak(const struct drm_edid *drm_edid)
313762306a36Sopenharmony_ci{
313862306a36Sopenharmony_ci	const struct detailed_timing *descriptor = NULL;
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.hfreq_start_khz * 2 : 0;
314562306a36Sopenharmony_ci}
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_cistatic int
314862306a36Sopenharmony_cidrm_gtf2_2c(const struct drm_edid *drm_edid)
314962306a36Sopenharmony_ci{
315062306a36Sopenharmony_ci	const struct detailed_timing *descriptor = NULL;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13);
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.c : 0;
315762306a36Sopenharmony_ci}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_cistatic int
316062306a36Sopenharmony_cidrm_gtf2_m(const struct drm_edid *drm_edid)
316162306a36Sopenharmony_ci{
316262306a36Sopenharmony_ci	const struct detailed_timing *descriptor = NULL;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14);
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	return descriptor ? le16_to_cpu(descriptor->data.other_data.data.range.formula.gtf2.m) : 0;
316962306a36Sopenharmony_ci}
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_cistatic int
317262306a36Sopenharmony_cidrm_gtf2_k(const struct drm_edid *drm_edid)
317362306a36Sopenharmony_ci{
317462306a36Sopenharmony_ci	const struct detailed_timing *descriptor = NULL;
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.k : 0;
318162306a36Sopenharmony_ci}
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_cistatic int
318462306a36Sopenharmony_cidrm_gtf2_2j(const struct drm_edid *drm_edid)
318562306a36Sopenharmony_ci{
318662306a36Sopenharmony_ci	const struct detailed_timing *descriptor = NULL;
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17);
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci	return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0;
319362306a36Sopenharmony_ci}
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_cistatic void
319662306a36Sopenharmony_ciget_timing_level(const struct detailed_timing *descriptor, void *data)
319762306a36Sopenharmony_ci{
319862306a36Sopenharmony_ci	int *res = data;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	if (!is_display_descriptor(descriptor, EDID_DETAIL_MONITOR_RANGE))
320162306a36Sopenharmony_ci		return;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.flags) != 10);
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	switch (descriptor->data.other_data.data.range.flags) {
320662306a36Sopenharmony_ci	case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG:
320762306a36Sopenharmony_ci		*res = LEVEL_GTF;
320862306a36Sopenharmony_ci		break;
320962306a36Sopenharmony_ci	case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG:
321062306a36Sopenharmony_ci		*res = LEVEL_GTF2;
321162306a36Sopenharmony_ci		break;
321262306a36Sopenharmony_ci	case DRM_EDID_CVT_SUPPORT_FLAG:
321362306a36Sopenharmony_ci		*res = LEVEL_CVT;
321462306a36Sopenharmony_ci		break;
321562306a36Sopenharmony_ci	default:
321662306a36Sopenharmony_ci		break;
321762306a36Sopenharmony_ci	}
321862306a36Sopenharmony_ci}
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci/* Get standard timing level (CVT/GTF/DMT). */
322162306a36Sopenharmony_cistatic int standard_timing_level(const struct drm_edid *drm_edid)
322262306a36Sopenharmony_ci{
322362306a36Sopenharmony_ci	const struct edid *edid = drm_edid->edid;
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci	if (edid->revision >= 4) {
322662306a36Sopenharmony_ci		/*
322762306a36Sopenharmony_ci		 * If the range descriptor doesn't
322862306a36Sopenharmony_ci		 * indicate otherwise default to CVT
322962306a36Sopenharmony_ci		 */
323062306a36Sopenharmony_ci		int ret = LEVEL_CVT;
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, get_timing_level, &ret);
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci		return ret;
323562306a36Sopenharmony_ci	} else if (edid->revision >= 3 && drm_gtf2_hbreak(drm_edid)) {
323662306a36Sopenharmony_ci		return LEVEL_GTF2;
323762306a36Sopenharmony_ci	} else if (edid->revision >= 2) {
323862306a36Sopenharmony_ci		return LEVEL_GTF;
323962306a36Sopenharmony_ci	} else {
324062306a36Sopenharmony_ci		return LEVEL_DMT;
324162306a36Sopenharmony_ci	}
324262306a36Sopenharmony_ci}
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci/*
324562306a36Sopenharmony_ci * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
324662306a36Sopenharmony_ci * monitors fill with ascii space (0x20) instead.
324762306a36Sopenharmony_ci */
324862306a36Sopenharmony_cistatic int
324962306a36Sopenharmony_cibad_std_timing(u8 a, u8 b)
325062306a36Sopenharmony_ci{
325162306a36Sopenharmony_ci	return (a == 0x00 && b == 0x00) ||
325262306a36Sopenharmony_ci	       (a == 0x01 && b == 0x01) ||
325362306a36Sopenharmony_ci	       (a == 0x20 && b == 0x20);
325462306a36Sopenharmony_ci}
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_cistatic int drm_mode_hsync(const struct drm_display_mode *mode)
325762306a36Sopenharmony_ci{
325862306a36Sopenharmony_ci	if (mode->htotal <= 0)
325962306a36Sopenharmony_ci		return 0;
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(mode->clock, mode->htotal);
326262306a36Sopenharmony_ci}
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_cistatic struct drm_display_mode *
326562306a36Sopenharmony_cidrm_gtf2_mode(struct drm_device *dev,
326662306a36Sopenharmony_ci	      const struct drm_edid *drm_edid,
326762306a36Sopenharmony_ci	      int hsize, int vsize, int vrefresh_rate)
326862306a36Sopenharmony_ci{
326962306a36Sopenharmony_ci	struct drm_display_mode *mode;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	/*
327262306a36Sopenharmony_ci	 * This is potentially wrong if there's ever a monitor with
327362306a36Sopenharmony_ci	 * more than one ranges section, each claiming a different
327462306a36Sopenharmony_ci	 * secondary GTF curve.  Please don't do that.
327562306a36Sopenharmony_ci	 */
327662306a36Sopenharmony_ci	mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
327762306a36Sopenharmony_ci	if (!mode)
327862306a36Sopenharmony_ci		return NULL;
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci	if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) {
328162306a36Sopenharmony_ci		drm_mode_destroy(dev, mode);
328262306a36Sopenharmony_ci		mode = drm_gtf_mode_complex(dev, hsize, vsize,
328362306a36Sopenharmony_ci					    vrefresh_rate, 0, 0,
328462306a36Sopenharmony_ci					    drm_gtf2_m(drm_edid),
328562306a36Sopenharmony_ci					    drm_gtf2_2c(drm_edid),
328662306a36Sopenharmony_ci					    drm_gtf2_k(drm_edid),
328762306a36Sopenharmony_ci					    drm_gtf2_2j(drm_edid));
328862306a36Sopenharmony_ci	}
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	return mode;
329162306a36Sopenharmony_ci}
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci/*
329462306a36Sopenharmony_ci * Take the standard timing params (in this case width, aspect, and refresh)
329562306a36Sopenharmony_ci * and convert them into a real mode using CVT/GTF/DMT.
329662306a36Sopenharmony_ci */
329762306a36Sopenharmony_cistatic struct drm_display_mode *drm_mode_std(struct drm_connector *connector,
329862306a36Sopenharmony_ci					     const struct drm_edid *drm_edid,
329962306a36Sopenharmony_ci					     const struct std_timing *t)
330062306a36Sopenharmony_ci{
330162306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
330262306a36Sopenharmony_ci	struct drm_display_mode *m, *mode = NULL;
330362306a36Sopenharmony_ci	int hsize, vsize;
330462306a36Sopenharmony_ci	int vrefresh_rate;
330562306a36Sopenharmony_ci	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
330662306a36Sopenharmony_ci		>> EDID_TIMING_ASPECT_SHIFT;
330762306a36Sopenharmony_ci	unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
330862306a36Sopenharmony_ci		>> EDID_TIMING_VFREQ_SHIFT;
330962306a36Sopenharmony_ci	int timing_level = standard_timing_level(drm_edid);
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	if (bad_std_timing(t->hsize, t->vfreq_aspect))
331262306a36Sopenharmony_ci		return NULL;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
331562306a36Sopenharmony_ci	hsize = t->hsize * 8 + 248;
331662306a36Sopenharmony_ci	/* vrefresh_rate = vfreq + 60 */
331762306a36Sopenharmony_ci	vrefresh_rate = vfreq + 60;
331862306a36Sopenharmony_ci	/* the vdisplay is calculated based on the aspect ratio */
331962306a36Sopenharmony_ci	if (aspect_ratio == 0) {
332062306a36Sopenharmony_ci		if (drm_edid->edid->revision < 3)
332162306a36Sopenharmony_ci			vsize = hsize;
332262306a36Sopenharmony_ci		else
332362306a36Sopenharmony_ci			vsize = (hsize * 10) / 16;
332462306a36Sopenharmony_ci	} else if (aspect_ratio == 1)
332562306a36Sopenharmony_ci		vsize = (hsize * 3) / 4;
332662306a36Sopenharmony_ci	else if (aspect_ratio == 2)
332762306a36Sopenharmony_ci		vsize = (hsize * 4) / 5;
332862306a36Sopenharmony_ci	else
332962306a36Sopenharmony_ci		vsize = (hsize * 9) / 16;
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	/* HDTV hack, part 1 */
333262306a36Sopenharmony_ci	if (vrefresh_rate == 60 &&
333362306a36Sopenharmony_ci	    ((hsize == 1360 && vsize == 765) ||
333462306a36Sopenharmony_ci	     (hsize == 1368 && vsize == 769))) {
333562306a36Sopenharmony_ci		hsize = 1366;
333662306a36Sopenharmony_ci		vsize = 768;
333762306a36Sopenharmony_ci	}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	/*
334062306a36Sopenharmony_ci	 * If this connector already has a mode for this size and refresh
334162306a36Sopenharmony_ci	 * rate (because it came from detailed or CVT info), use that
334262306a36Sopenharmony_ci	 * instead.  This way we don't have to guess at interlace or
334362306a36Sopenharmony_ci	 * reduced blanking.
334462306a36Sopenharmony_ci	 */
334562306a36Sopenharmony_ci	list_for_each_entry(m, &connector->probed_modes, head)
334662306a36Sopenharmony_ci		if (m->hdisplay == hsize && m->vdisplay == vsize &&
334762306a36Sopenharmony_ci		    drm_mode_vrefresh(m) == vrefresh_rate)
334862306a36Sopenharmony_ci			return NULL;
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	/* HDTV hack, part 2 */
335162306a36Sopenharmony_ci	if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
335262306a36Sopenharmony_ci		mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
335362306a36Sopenharmony_ci				    false);
335462306a36Sopenharmony_ci		if (!mode)
335562306a36Sopenharmony_ci			return NULL;
335662306a36Sopenharmony_ci		mode->hdisplay = 1366;
335762306a36Sopenharmony_ci		mode->hsync_start = mode->hsync_start - 1;
335862306a36Sopenharmony_ci		mode->hsync_end = mode->hsync_end - 1;
335962306a36Sopenharmony_ci		return mode;
336062306a36Sopenharmony_ci	}
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	/* check whether it can be found in default mode table */
336362306a36Sopenharmony_ci	if (drm_monitor_supports_rb(drm_edid)) {
336462306a36Sopenharmony_ci		mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
336562306a36Sopenharmony_ci					 true);
336662306a36Sopenharmony_ci		if (mode)
336762306a36Sopenharmony_ci			return mode;
336862306a36Sopenharmony_ci	}
336962306a36Sopenharmony_ci	mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false);
337062306a36Sopenharmony_ci	if (mode)
337162306a36Sopenharmony_ci		return mode;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	/* okay, generate it */
337462306a36Sopenharmony_ci	switch (timing_level) {
337562306a36Sopenharmony_ci	case LEVEL_DMT:
337662306a36Sopenharmony_ci		break;
337762306a36Sopenharmony_ci	case LEVEL_GTF:
337862306a36Sopenharmony_ci		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
337962306a36Sopenharmony_ci		break;
338062306a36Sopenharmony_ci	case LEVEL_GTF2:
338162306a36Sopenharmony_ci		mode = drm_gtf2_mode(dev, drm_edid, hsize, vsize, vrefresh_rate);
338262306a36Sopenharmony_ci		break;
338362306a36Sopenharmony_ci	case LEVEL_CVT:
338462306a36Sopenharmony_ci		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
338562306a36Sopenharmony_ci				    false);
338662306a36Sopenharmony_ci		break;
338762306a36Sopenharmony_ci	}
338862306a36Sopenharmony_ci	return mode;
338962306a36Sopenharmony_ci}
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci/*
339262306a36Sopenharmony_ci * EDID is delightfully ambiguous about how interlaced modes are to be
339362306a36Sopenharmony_ci * encoded.  Our internal representation is of frame height, but some
339462306a36Sopenharmony_ci * HDTV detailed timings are encoded as field height.
339562306a36Sopenharmony_ci *
339662306a36Sopenharmony_ci * The format list here is from CEA, in frame size.  Technically we
339762306a36Sopenharmony_ci * should be checking refresh rate too.  Whatever.
339862306a36Sopenharmony_ci */
339962306a36Sopenharmony_cistatic void
340062306a36Sopenharmony_cidrm_mode_do_interlace_quirk(struct drm_display_mode *mode,
340162306a36Sopenharmony_ci			    const struct detailed_pixel_timing *pt)
340262306a36Sopenharmony_ci{
340362306a36Sopenharmony_ci	int i;
340462306a36Sopenharmony_ci	static const struct {
340562306a36Sopenharmony_ci		int w, h;
340662306a36Sopenharmony_ci	} cea_interlaced[] = {
340762306a36Sopenharmony_ci		{ 1920, 1080 },
340862306a36Sopenharmony_ci		{  720,  480 },
340962306a36Sopenharmony_ci		{ 1440,  480 },
341062306a36Sopenharmony_ci		{ 2880,  480 },
341162306a36Sopenharmony_ci		{  720,  576 },
341262306a36Sopenharmony_ci		{ 1440,  576 },
341362306a36Sopenharmony_ci		{ 2880,  576 },
341462306a36Sopenharmony_ci	};
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
341762306a36Sopenharmony_ci		return;
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
342062306a36Sopenharmony_ci		if ((mode->hdisplay == cea_interlaced[i].w) &&
342162306a36Sopenharmony_ci		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
342262306a36Sopenharmony_ci			mode->vdisplay *= 2;
342362306a36Sopenharmony_ci			mode->vsync_start *= 2;
342462306a36Sopenharmony_ci			mode->vsync_end *= 2;
342562306a36Sopenharmony_ci			mode->vtotal *= 2;
342662306a36Sopenharmony_ci			mode->vtotal |= 1;
342762306a36Sopenharmony_ci		}
342862306a36Sopenharmony_ci	}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	mode->flags |= DRM_MODE_FLAG_INTERLACE;
343162306a36Sopenharmony_ci}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci/*
343462306a36Sopenharmony_ci * Create a new mode from an EDID detailed timing section. An EDID detailed
343562306a36Sopenharmony_ci * timing block contains enough info for us to create and return a new struct
343662306a36Sopenharmony_ci * drm_display_mode.
343762306a36Sopenharmony_ci */
343862306a36Sopenharmony_cistatic struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector,
343962306a36Sopenharmony_ci						  const struct drm_edid *drm_edid,
344062306a36Sopenharmony_ci						  const struct detailed_timing *timing)
344162306a36Sopenharmony_ci{
344262306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
344362306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
344462306a36Sopenharmony_ci	struct drm_display_mode *mode;
344562306a36Sopenharmony_ci	const struct detailed_pixel_timing *pt = &timing->data.pixel_data;
344662306a36Sopenharmony_ci	unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
344762306a36Sopenharmony_ci	unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
344862306a36Sopenharmony_ci	unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
344962306a36Sopenharmony_ci	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;
345062306a36Sopenharmony_ci	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;
345162306a36Sopenharmony_ci	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
345262306a36Sopenharmony_ci	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;
345362306a36Sopenharmony_ci	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	/* ignore tiny modes */
345662306a36Sopenharmony_ci	if (hactive < 64 || vactive < 64)
345762306a36Sopenharmony_ci		return NULL;
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	if (pt->misc & DRM_EDID_PT_STEREO) {
346062306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Stereo mode not supported\n",
346162306a36Sopenharmony_ci			    connector->base.id, connector->name);
346262306a36Sopenharmony_ci		return NULL;
346362306a36Sopenharmony_ci	}
346462306a36Sopenharmony_ci	if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
346562306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Composite sync not supported\n",
346662306a36Sopenharmony_ci			    connector->base.id, connector->name);
346762306a36Sopenharmony_ci	}
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	/* it is incorrect if hsync/vsync width is zero */
347062306a36Sopenharmony_ci	if (!hsync_pulse_width || !vsync_pulse_width) {
347162306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Incorrect Detailed timing. Wrong Hsync/Vsync pulse width\n",
347262306a36Sopenharmony_ci			    connector->base.id, connector->name);
347362306a36Sopenharmony_ci		return NULL;
347462306a36Sopenharmony_ci	}
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
347762306a36Sopenharmony_ci		mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
347862306a36Sopenharmony_ci		if (!mode)
347962306a36Sopenharmony_ci			return NULL;
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci		goto set_size;
348262306a36Sopenharmony_ci	}
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	mode = drm_mode_create(dev);
348562306a36Sopenharmony_ci	if (!mode)
348662306a36Sopenharmony_ci		return NULL;
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
348962306a36Sopenharmony_ci		mode->clock = 1088 * 10;
349062306a36Sopenharmony_ci	else
349162306a36Sopenharmony_ci		mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	mode->hdisplay = hactive;
349462306a36Sopenharmony_ci	mode->hsync_start = mode->hdisplay + hsync_offset;
349562306a36Sopenharmony_ci	mode->hsync_end = mode->hsync_start + hsync_pulse_width;
349662306a36Sopenharmony_ci	mode->htotal = mode->hdisplay + hblank;
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	mode->vdisplay = vactive;
349962306a36Sopenharmony_ci	mode->vsync_start = mode->vdisplay + vsync_offset;
350062306a36Sopenharmony_ci	mode->vsync_end = mode->vsync_start + vsync_pulse_width;
350162306a36Sopenharmony_ci	mode->vtotal = mode->vdisplay + vblank;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	/* Some EDIDs have bogus h/vsync_end values */
350462306a36Sopenharmony_ci	if (mode->hsync_end > mode->htotal) {
350562306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing hsync_end %d->%d\n",
350662306a36Sopenharmony_ci			    connector->base.id, connector->name,
350762306a36Sopenharmony_ci			    mode->hsync_end, mode->htotal);
350862306a36Sopenharmony_ci		mode->hsync_end = mode->htotal;
350962306a36Sopenharmony_ci	}
351062306a36Sopenharmony_ci	if (mode->vsync_end > mode->vtotal) {
351162306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing vsync_end %d->%d\n",
351262306a36Sopenharmony_ci			    connector->base.id, connector->name,
351362306a36Sopenharmony_ci			    mode->vsync_end, mode->vtotal);
351462306a36Sopenharmony_ci		mode->vsync_end = mode->vtotal;
351562306a36Sopenharmony_ci	}
351662306a36Sopenharmony_ci
351762306a36Sopenharmony_ci	drm_mode_do_interlace_quirk(mode, pt);
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
352062306a36Sopenharmony_ci		mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
352162306a36Sopenharmony_ci	} else {
352262306a36Sopenharmony_ci		mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
352362306a36Sopenharmony_ci			DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
352462306a36Sopenharmony_ci		mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
352562306a36Sopenharmony_ci			DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
352662306a36Sopenharmony_ci	}
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ciset_size:
352962306a36Sopenharmony_ci	mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
353062306a36Sopenharmony_ci	mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_DETAILED_IN_CM) {
353362306a36Sopenharmony_ci		mode->width_mm *= 10;
353462306a36Sopenharmony_ci		mode->height_mm *= 10;
353562306a36Sopenharmony_ci	}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
353862306a36Sopenharmony_ci		mode->width_mm = drm_edid->edid->width_cm * 10;
353962306a36Sopenharmony_ci		mode->height_mm = drm_edid->edid->height_cm * 10;
354062306a36Sopenharmony_ci	}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci	mode->type = DRM_MODE_TYPE_DRIVER;
354362306a36Sopenharmony_ci	drm_mode_set_name(mode);
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	return mode;
354662306a36Sopenharmony_ci}
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_cistatic bool
354962306a36Sopenharmony_cimode_in_hsync_range(const struct drm_display_mode *mode,
355062306a36Sopenharmony_ci		    const struct edid *edid, const u8 *t)
355162306a36Sopenharmony_ci{
355262306a36Sopenharmony_ci	int hsync, hmin, hmax;
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	hmin = t[7];
355562306a36Sopenharmony_ci	if (edid->revision >= 4)
355662306a36Sopenharmony_ci	    hmin += ((t[4] & 0x04) ? 255 : 0);
355762306a36Sopenharmony_ci	hmax = t[8];
355862306a36Sopenharmony_ci	if (edid->revision >= 4)
355962306a36Sopenharmony_ci	    hmax += ((t[4] & 0x08) ? 255 : 0);
356062306a36Sopenharmony_ci	hsync = drm_mode_hsync(mode);
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci	return (hsync <= hmax && hsync >= hmin);
356362306a36Sopenharmony_ci}
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_cistatic bool
356662306a36Sopenharmony_cimode_in_vsync_range(const struct drm_display_mode *mode,
356762306a36Sopenharmony_ci		    const struct edid *edid, const u8 *t)
356862306a36Sopenharmony_ci{
356962306a36Sopenharmony_ci	int vsync, vmin, vmax;
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ci	vmin = t[5];
357262306a36Sopenharmony_ci	if (edid->revision >= 4)
357362306a36Sopenharmony_ci	    vmin += ((t[4] & 0x01) ? 255 : 0);
357462306a36Sopenharmony_ci	vmax = t[6];
357562306a36Sopenharmony_ci	if (edid->revision >= 4)
357662306a36Sopenharmony_ci	    vmax += ((t[4] & 0x02) ? 255 : 0);
357762306a36Sopenharmony_ci	vsync = drm_mode_vrefresh(mode);
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	return (vsync <= vmax && vsync >= vmin);
358062306a36Sopenharmony_ci}
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_cistatic u32
358362306a36Sopenharmony_cirange_pixel_clock(const struct edid *edid, const u8 *t)
358462306a36Sopenharmony_ci{
358562306a36Sopenharmony_ci	/* unspecified */
358662306a36Sopenharmony_ci	if (t[9] == 0 || t[9] == 255)
358762306a36Sopenharmony_ci		return 0;
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	/* 1.4 with CVT support gives us real precision, yay */
359062306a36Sopenharmony_ci	if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG)
359162306a36Sopenharmony_ci		return (t[9] * 10000) - ((t[12] >> 2) * 250);
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	/* 1.3 is pathetic, so fuzz up a bit */
359462306a36Sopenharmony_ci	return t[9] * 10000 + 5001;
359562306a36Sopenharmony_ci}
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_cistatic bool mode_in_range(const struct drm_display_mode *mode,
359862306a36Sopenharmony_ci			  const struct drm_edid *drm_edid,
359962306a36Sopenharmony_ci			  const struct detailed_timing *timing)
360062306a36Sopenharmony_ci{
360162306a36Sopenharmony_ci	const struct edid *edid = drm_edid->edid;
360262306a36Sopenharmony_ci	u32 max_clock;
360362306a36Sopenharmony_ci	const u8 *t = (const u8 *)timing;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	if (!mode_in_hsync_range(mode, edid, t))
360662306a36Sopenharmony_ci		return false;
360762306a36Sopenharmony_ci
360862306a36Sopenharmony_ci	if (!mode_in_vsync_range(mode, edid, t))
360962306a36Sopenharmony_ci		return false;
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	if ((max_clock = range_pixel_clock(edid, t)))
361262306a36Sopenharmony_ci		if (mode->clock > max_clock)
361362306a36Sopenharmony_ci			return false;
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	/* 1.4 max horizontal check */
361662306a36Sopenharmony_ci	if (edid->revision >= 4 && t[10] == DRM_EDID_CVT_SUPPORT_FLAG)
361762306a36Sopenharmony_ci		if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
361862306a36Sopenharmony_ci			return false;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	if (mode_is_rb(mode) && !drm_monitor_supports_rb(drm_edid))
362162306a36Sopenharmony_ci		return false;
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci	return true;
362462306a36Sopenharmony_ci}
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_cistatic bool valid_inferred_mode(const struct drm_connector *connector,
362762306a36Sopenharmony_ci				const struct drm_display_mode *mode)
362862306a36Sopenharmony_ci{
362962306a36Sopenharmony_ci	const struct drm_display_mode *m;
363062306a36Sopenharmony_ci	bool ok = false;
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci	list_for_each_entry(m, &connector->probed_modes, head) {
363362306a36Sopenharmony_ci		if (mode->hdisplay == m->hdisplay &&
363462306a36Sopenharmony_ci		    mode->vdisplay == m->vdisplay &&
363562306a36Sopenharmony_ci		    drm_mode_vrefresh(mode) == drm_mode_vrefresh(m))
363662306a36Sopenharmony_ci			return false; /* duplicated */
363762306a36Sopenharmony_ci		if (mode->hdisplay <= m->hdisplay &&
363862306a36Sopenharmony_ci		    mode->vdisplay <= m->vdisplay)
363962306a36Sopenharmony_ci			ok = true;
364062306a36Sopenharmony_ci	}
364162306a36Sopenharmony_ci	return ok;
364262306a36Sopenharmony_ci}
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_cistatic int drm_dmt_modes_for_range(struct drm_connector *connector,
364562306a36Sopenharmony_ci				   const struct drm_edid *drm_edid,
364662306a36Sopenharmony_ci				   const struct detailed_timing *timing)
364762306a36Sopenharmony_ci{
364862306a36Sopenharmony_ci	int i, modes = 0;
364962306a36Sopenharmony_ci	struct drm_display_mode *newmode;
365062306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
365362306a36Sopenharmony_ci		if (mode_in_range(drm_dmt_modes + i, drm_edid, timing) &&
365462306a36Sopenharmony_ci		    valid_inferred_mode(connector, drm_dmt_modes + i)) {
365562306a36Sopenharmony_ci			newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
365662306a36Sopenharmony_ci			if (newmode) {
365762306a36Sopenharmony_ci				drm_mode_probed_add(connector, newmode);
365862306a36Sopenharmony_ci				modes++;
365962306a36Sopenharmony_ci			}
366062306a36Sopenharmony_ci		}
366162306a36Sopenharmony_ci	}
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	return modes;
366462306a36Sopenharmony_ci}
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci/* fix up 1366x768 mode from 1368x768;
366762306a36Sopenharmony_ci * GFT/CVT can't express 1366 width which isn't dividable by 8
366862306a36Sopenharmony_ci */
366962306a36Sopenharmony_civoid drm_mode_fixup_1366x768(struct drm_display_mode *mode)
367062306a36Sopenharmony_ci{
367162306a36Sopenharmony_ci	if (mode->hdisplay == 1368 && mode->vdisplay == 768) {
367262306a36Sopenharmony_ci		mode->hdisplay = 1366;
367362306a36Sopenharmony_ci		mode->hsync_start--;
367462306a36Sopenharmony_ci		mode->hsync_end--;
367562306a36Sopenharmony_ci		drm_mode_set_name(mode);
367662306a36Sopenharmony_ci	}
367762306a36Sopenharmony_ci}
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_cistatic int drm_gtf_modes_for_range(struct drm_connector *connector,
368062306a36Sopenharmony_ci				   const struct drm_edid *drm_edid,
368162306a36Sopenharmony_ci				   const struct detailed_timing *timing)
368262306a36Sopenharmony_ci{
368362306a36Sopenharmony_ci	int i, modes = 0;
368462306a36Sopenharmony_ci	struct drm_display_mode *newmode;
368562306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
368862306a36Sopenharmony_ci		const struct minimode *m = &extra_modes[i];
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci		newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
369162306a36Sopenharmony_ci		if (!newmode)
369262306a36Sopenharmony_ci			return modes;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci		drm_mode_fixup_1366x768(newmode);
369562306a36Sopenharmony_ci		if (!mode_in_range(newmode, drm_edid, timing) ||
369662306a36Sopenharmony_ci		    !valid_inferred_mode(connector, newmode)) {
369762306a36Sopenharmony_ci			drm_mode_destroy(dev, newmode);
369862306a36Sopenharmony_ci			continue;
369962306a36Sopenharmony_ci		}
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci		drm_mode_probed_add(connector, newmode);
370262306a36Sopenharmony_ci		modes++;
370362306a36Sopenharmony_ci	}
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	return modes;
370662306a36Sopenharmony_ci}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_cistatic int drm_gtf2_modes_for_range(struct drm_connector *connector,
370962306a36Sopenharmony_ci				    const struct drm_edid *drm_edid,
371062306a36Sopenharmony_ci				    const struct detailed_timing *timing)
371162306a36Sopenharmony_ci{
371262306a36Sopenharmony_ci	int i, modes = 0;
371362306a36Sopenharmony_ci	struct drm_display_mode *newmode;
371462306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
371562306a36Sopenharmony_ci
371662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
371762306a36Sopenharmony_ci		const struct minimode *m = &extra_modes[i];
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci		newmode = drm_gtf2_mode(dev, drm_edid, m->w, m->h, m->r);
372062306a36Sopenharmony_ci		if (!newmode)
372162306a36Sopenharmony_ci			return modes;
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci		drm_mode_fixup_1366x768(newmode);
372462306a36Sopenharmony_ci		if (!mode_in_range(newmode, drm_edid, timing) ||
372562306a36Sopenharmony_ci		    !valid_inferred_mode(connector, newmode)) {
372662306a36Sopenharmony_ci			drm_mode_destroy(dev, newmode);
372762306a36Sopenharmony_ci			continue;
372862306a36Sopenharmony_ci		}
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci		drm_mode_probed_add(connector, newmode);
373162306a36Sopenharmony_ci		modes++;
373262306a36Sopenharmony_ci	}
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci	return modes;
373562306a36Sopenharmony_ci}
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_cistatic int drm_cvt_modes_for_range(struct drm_connector *connector,
373862306a36Sopenharmony_ci				   const struct drm_edid *drm_edid,
373962306a36Sopenharmony_ci				   const struct detailed_timing *timing)
374062306a36Sopenharmony_ci{
374162306a36Sopenharmony_ci	int i, modes = 0;
374262306a36Sopenharmony_ci	struct drm_display_mode *newmode;
374362306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
374462306a36Sopenharmony_ci	bool rb = drm_monitor_supports_rb(drm_edid);
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
374762306a36Sopenharmony_ci		const struct minimode *m = &extra_modes[i];
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci		newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
375062306a36Sopenharmony_ci		if (!newmode)
375162306a36Sopenharmony_ci			return modes;
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci		drm_mode_fixup_1366x768(newmode);
375462306a36Sopenharmony_ci		if (!mode_in_range(newmode, drm_edid, timing) ||
375562306a36Sopenharmony_ci		    !valid_inferred_mode(connector, newmode)) {
375662306a36Sopenharmony_ci			drm_mode_destroy(dev, newmode);
375762306a36Sopenharmony_ci			continue;
375862306a36Sopenharmony_ci		}
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci		drm_mode_probed_add(connector, newmode);
376162306a36Sopenharmony_ci		modes++;
376262306a36Sopenharmony_ci	}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	return modes;
376562306a36Sopenharmony_ci}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_cistatic void
376862306a36Sopenharmony_cido_inferred_modes(const struct detailed_timing *timing, void *c)
376962306a36Sopenharmony_ci{
377062306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
377162306a36Sopenharmony_ci	const struct detailed_non_pixel *data = &timing->data.other_data;
377262306a36Sopenharmony_ci	const struct detailed_data_monitor_range *range = &data->data.range;
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
377562306a36Sopenharmony_ci		return;
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	closure->modes += drm_dmt_modes_for_range(closure->connector,
377862306a36Sopenharmony_ci						  closure->drm_edid,
377962306a36Sopenharmony_ci						  timing);
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	if (closure->drm_edid->edid->revision < 2)
378262306a36Sopenharmony_ci		return; /* GTF not defined yet */
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	switch (range->flags) {
378562306a36Sopenharmony_ci	case DRM_EDID_SECONDARY_GTF_SUPPORT_FLAG:
378662306a36Sopenharmony_ci		closure->modes += drm_gtf2_modes_for_range(closure->connector,
378762306a36Sopenharmony_ci							   closure->drm_edid,
378862306a36Sopenharmony_ci							   timing);
378962306a36Sopenharmony_ci		break;
379062306a36Sopenharmony_ci	case DRM_EDID_DEFAULT_GTF_SUPPORT_FLAG:
379162306a36Sopenharmony_ci		closure->modes += drm_gtf_modes_for_range(closure->connector,
379262306a36Sopenharmony_ci							  closure->drm_edid,
379362306a36Sopenharmony_ci							  timing);
379462306a36Sopenharmony_ci		break;
379562306a36Sopenharmony_ci	case DRM_EDID_CVT_SUPPORT_FLAG:
379662306a36Sopenharmony_ci		if (closure->drm_edid->edid->revision < 4)
379762306a36Sopenharmony_ci			break;
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci		closure->modes += drm_cvt_modes_for_range(closure->connector,
380062306a36Sopenharmony_ci							  closure->drm_edid,
380162306a36Sopenharmony_ci							  timing);
380262306a36Sopenharmony_ci		break;
380362306a36Sopenharmony_ci	case DRM_EDID_RANGE_LIMITS_ONLY_FLAG:
380462306a36Sopenharmony_ci	default:
380562306a36Sopenharmony_ci		break;
380662306a36Sopenharmony_ci	}
380762306a36Sopenharmony_ci}
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_cistatic int add_inferred_modes(struct drm_connector *connector,
381062306a36Sopenharmony_ci			      const struct drm_edid *drm_edid)
381162306a36Sopenharmony_ci{
381262306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
381362306a36Sopenharmony_ci		.connector = connector,
381462306a36Sopenharmony_ci		.drm_edid = drm_edid,
381562306a36Sopenharmony_ci	};
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	if (drm_edid->edid->revision >= 1)
381862306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure);
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci	return closure.modes;
382162306a36Sopenharmony_ci}
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_cistatic int
382462306a36Sopenharmony_cidrm_est3_modes(struct drm_connector *connector, const struct detailed_timing *timing)
382562306a36Sopenharmony_ci{
382662306a36Sopenharmony_ci	int i, j, m, modes = 0;
382762306a36Sopenharmony_ci	struct drm_display_mode *mode;
382862306a36Sopenharmony_ci	const u8 *est = ((const u8 *)timing) + 6;
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
383162306a36Sopenharmony_ci		for (j = 7; j >= 0; j--) {
383262306a36Sopenharmony_ci			m = (i * 8) + (7 - j);
383362306a36Sopenharmony_ci			if (m >= ARRAY_SIZE(est3_modes))
383462306a36Sopenharmony_ci				break;
383562306a36Sopenharmony_ci			if (est[i] & (1 << j)) {
383662306a36Sopenharmony_ci				mode = drm_mode_find_dmt(connector->dev,
383762306a36Sopenharmony_ci							 est3_modes[m].w,
383862306a36Sopenharmony_ci							 est3_modes[m].h,
383962306a36Sopenharmony_ci							 est3_modes[m].r,
384062306a36Sopenharmony_ci							 est3_modes[m].rb);
384162306a36Sopenharmony_ci				if (mode) {
384262306a36Sopenharmony_ci					drm_mode_probed_add(connector, mode);
384362306a36Sopenharmony_ci					modes++;
384462306a36Sopenharmony_ci				}
384562306a36Sopenharmony_ci			}
384662306a36Sopenharmony_ci		}
384762306a36Sopenharmony_ci	}
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ci	return modes;
385062306a36Sopenharmony_ci}
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_cistatic void
385362306a36Sopenharmony_cido_established_modes(const struct detailed_timing *timing, void *c)
385462306a36Sopenharmony_ci{
385562306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_EST_TIMINGS))
385862306a36Sopenharmony_ci		return;
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	closure->modes += drm_est3_modes(closure->connector, timing);
386162306a36Sopenharmony_ci}
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci/*
386462306a36Sopenharmony_ci * Get established modes from EDID and add them. Each EDID block contains a
386562306a36Sopenharmony_ci * bitmap of the supported "established modes" list (defined above). Tease them
386662306a36Sopenharmony_ci * out and add them to the global modes list.
386762306a36Sopenharmony_ci */
386862306a36Sopenharmony_cistatic int add_established_modes(struct drm_connector *connector,
386962306a36Sopenharmony_ci				 const struct drm_edid *drm_edid)
387062306a36Sopenharmony_ci{
387162306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
387262306a36Sopenharmony_ci	const struct edid *edid = drm_edid->edid;
387362306a36Sopenharmony_ci	unsigned long est_bits = edid->established_timings.t1 |
387462306a36Sopenharmony_ci		(edid->established_timings.t2 << 8) |
387562306a36Sopenharmony_ci		((edid->established_timings.mfg_rsvd & 0x80) << 9);
387662306a36Sopenharmony_ci	int i, modes = 0;
387762306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
387862306a36Sopenharmony_ci		.connector = connector,
387962306a36Sopenharmony_ci		.drm_edid = drm_edid,
388062306a36Sopenharmony_ci	};
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci	for (i = 0; i <= EDID_EST_TIMINGS; i++) {
388362306a36Sopenharmony_ci		if (est_bits & (1<<i)) {
388462306a36Sopenharmony_ci			struct drm_display_mode *newmode;
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
388762306a36Sopenharmony_ci			if (newmode) {
388862306a36Sopenharmony_ci				drm_mode_probed_add(connector, newmode);
388962306a36Sopenharmony_ci				modes++;
389062306a36Sopenharmony_ci			}
389162306a36Sopenharmony_ci		}
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	if (edid->revision >= 1)
389562306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, do_established_modes,
389662306a36Sopenharmony_ci					    &closure);
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	return modes + closure.modes;
389962306a36Sopenharmony_ci}
390062306a36Sopenharmony_ci
390162306a36Sopenharmony_cistatic void
390262306a36Sopenharmony_cido_standard_modes(const struct detailed_timing *timing, void *c)
390362306a36Sopenharmony_ci{
390462306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
390562306a36Sopenharmony_ci	const struct detailed_non_pixel *data = &timing->data.other_data;
390662306a36Sopenharmony_ci	struct drm_connector *connector = closure->connector;
390762306a36Sopenharmony_ci	int i;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES))
391062306a36Sopenharmony_ci		return;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
391362306a36Sopenharmony_ci		const struct std_timing *std = &data->data.timings[i];
391462306a36Sopenharmony_ci		struct drm_display_mode *newmode;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci		newmode = drm_mode_std(connector, closure->drm_edid, std);
391762306a36Sopenharmony_ci		if (newmode) {
391862306a36Sopenharmony_ci			drm_mode_probed_add(connector, newmode);
391962306a36Sopenharmony_ci			closure->modes++;
392062306a36Sopenharmony_ci		}
392162306a36Sopenharmony_ci	}
392262306a36Sopenharmony_ci}
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci/*
392562306a36Sopenharmony_ci * Get standard modes from EDID and add them. Standard modes can be calculated
392662306a36Sopenharmony_ci * using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and
392762306a36Sopenharmony_ci * add them to the list.
392862306a36Sopenharmony_ci */
392962306a36Sopenharmony_cistatic int add_standard_modes(struct drm_connector *connector,
393062306a36Sopenharmony_ci			      const struct drm_edid *drm_edid)
393162306a36Sopenharmony_ci{
393262306a36Sopenharmony_ci	int i, modes = 0;
393362306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
393462306a36Sopenharmony_ci		.connector = connector,
393562306a36Sopenharmony_ci		.drm_edid = drm_edid,
393662306a36Sopenharmony_ci	};
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci	for (i = 0; i < EDID_STD_TIMINGS; i++) {
393962306a36Sopenharmony_ci		struct drm_display_mode *newmode;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci		newmode = drm_mode_std(connector, drm_edid,
394262306a36Sopenharmony_ci				       &drm_edid->edid->standard_timings[i]);
394362306a36Sopenharmony_ci		if (newmode) {
394462306a36Sopenharmony_ci			drm_mode_probed_add(connector, newmode);
394562306a36Sopenharmony_ci			modes++;
394662306a36Sopenharmony_ci		}
394762306a36Sopenharmony_ci	}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	if (drm_edid->edid->revision >= 1)
395062306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, do_standard_modes,
395162306a36Sopenharmony_ci					    &closure);
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci	/* XXX should also look for standard codes in VTB blocks */
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	return modes + closure.modes;
395662306a36Sopenharmony_ci}
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_cistatic int drm_cvt_modes(struct drm_connector *connector,
395962306a36Sopenharmony_ci			 const struct detailed_timing *timing)
396062306a36Sopenharmony_ci{
396162306a36Sopenharmony_ci	int i, j, modes = 0;
396262306a36Sopenharmony_ci	struct drm_display_mode *newmode;
396362306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
396462306a36Sopenharmony_ci	const struct cvt_timing *cvt;
396562306a36Sopenharmony_ci	static const int rates[] = { 60, 85, 75, 60, 50 };
396662306a36Sopenharmony_ci	const u8 empty[3] = { 0, 0, 0 };
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
396962306a36Sopenharmony_ci		int width, height;
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_ci		cvt = &(timing->data.other_data.data.cvt[i]);
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci		if (!memcmp(cvt->code, empty, 3))
397462306a36Sopenharmony_ci			continue;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci		height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
397762306a36Sopenharmony_ci		switch (cvt->code[1] & 0x0c) {
397862306a36Sopenharmony_ci		/* default - because compiler doesn't see that we've enumerated all cases */
397962306a36Sopenharmony_ci		default:
398062306a36Sopenharmony_ci		case 0x00:
398162306a36Sopenharmony_ci			width = height * 4 / 3;
398262306a36Sopenharmony_ci			break;
398362306a36Sopenharmony_ci		case 0x04:
398462306a36Sopenharmony_ci			width = height * 16 / 9;
398562306a36Sopenharmony_ci			break;
398662306a36Sopenharmony_ci		case 0x08:
398762306a36Sopenharmony_ci			width = height * 16 / 10;
398862306a36Sopenharmony_ci			break;
398962306a36Sopenharmony_ci		case 0x0c:
399062306a36Sopenharmony_ci			width = height * 15 / 9;
399162306a36Sopenharmony_ci			break;
399262306a36Sopenharmony_ci		}
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ci		for (j = 1; j < 5; j++) {
399562306a36Sopenharmony_ci			if (cvt->code[2] & (1 << j)) {
399662306a36Sopenharmony_ci				newmode = drm_cvt_mode(dev, width, height,
399762306a36Sopenharmony_ci						       rates[j], j == 0,
399862306a36Sopenharmony_ci						       false, false);
399962306a36Sopenharmony_ci				if (newmode) {
400062306a36Sopenharmony_ci					drm_mode_probed_add(connector, newmode);
400162306a36Sopenharmony_ci					modes++;
400262306a36Sopenharmony_ci				}
400362306a36Sopenharmony_ci			}
400462306a36Sopenharmony_ci		}
400562306a36Sopenharmony_ci	}
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci	return modes;
400862306a36Sopenharmony_ci}
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_cistatic void
401162306a36Sopenharmony_cido_cvt_mode(const struct detailed_timing *timing, void *c)
401262306a36Sopenharmony_ci{
401362306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_CVT_3BYTE))
401662306a36Sopenharmony_ci		return;
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	closure->modes += drm_cvt_modes(closure->connector, timing);
401962306a36Sopenharmony_ci}
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_cistatic int
402262306a36Sopenharmony_ciadd_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid)
402362306a36Sopenharmony_ci{
402462306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
402562306a36Sopenharmony_ci		.connector = connector,
402662306a36Sopenharmony_ci		.drm_edid = drm_edid,
402762306a36Sopenharmony_ci	};
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	if (drm_edid->edid->revision >= 3)
403062306a36Sopenharmony_ci		drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure);
403162306a36Sopenharmony_ci
403262306a36Sopenharmony_ci	/* XXX should also look for CVT codes in VTB blocks */
403362306a36Sopenharmony_ci
403462306a36Sopenharmony_ci	return closure.modes;
403562306a36Sopenharmony_ci}
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_cistatic void fixup_detailed_cea_mode_clock(struct drm_connector *connector,
403862306a36Sopenharmony_ci					  struct drm_display_mode *mode);
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_cistatic void
404162306a36Sopenharmony_cido_detailed_mode(const struct detailed_timing *timing, void *c)
404262306a36Sopenharmony_ci{
404362306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
404462306a36Sopenharmony_ci	struct drm_display_mode *newmode;
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	if (!is_detailed_timing_descriptor(timing))
404762306a36Sopenharmony_ci		return;
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	newmode = drm_mode_detailed(closure->connector,
405062306a36Sopenharmony_ci				    closure->drm_edid, timing);
405162306a36Sopenharmony_ci	if (!newmode)
405262306a36Sopenharmony_ci		return;
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci	if (closure->preferred)
405562306a36Sopenharmony_ci		newmode->type |= DRM_MODE_TYPE_PREFERRED;
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	/*
405862306a36Sopenharmony_ci	 * Detailed modes are limited to 10kHz pixel clock resolution,
405962306a36Sopenharmony_ci	 * so fix up anything that looks like CEA/HDMI mode, but the clock
406062306a36Sopenharmony_ci	 * is just slightly off.
406162306a36Sopenharmony_ci	 */
406262306a36Sopenharmony_ci	fixup_detailed_cea_mode_clock(closure->connector, newmode);
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci	drm_mode_probed_add(closure->connector, newmode);
406562306a36Sopenharmony_ci	closure->modes++;
406662306a36Sopenharmony_ci	closure->preferred = false;
406762306a36Sopenharmony_ci}
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci/*
407062306a36Sopenharmony_ci * add_detailed_modes - Add modes from detailed timings
407162306a36Sopenharmony_ci * @connector: attached connector
407262306a36Sopenharmony_ci * @drm_edid: EDID block to scan
407362306a36Sopenharmony_ci */
407462306a36Sopenharmony_cistatic int add_detailed_modes(struct drm_connector *connector,
407562306a36Sopenharmony_ci			      const struct drm_edid *drm_edid)
407662306a36Sopenharmony_ci{
407762306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
407862306a36Sopenharmony_ci		.connector = connector,
407962306a36Sopenharmony_ci		.drm_edid = drm_edid,
408062306a36Sopenharmony_ci	};
408162306a36Sopenharmony_ci
408262306a36Sopenharmony_ci	if (drm_edid->edid->revision >= 4)
408362306a36Sopenharmony_ci		closure.preferred = true; /* first detailed timing is always preferred */
408462306a36Sopenharmony_ci	else
408562306a36Sopenharmony_ci		closure.preferred =
408662306a36Sopenharmony_ci			drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING;
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure);
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	return closure.modes;
409162306a36Sopenharmony_ci}
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci/* CTA-861-H Table 60 - CTA Tag Codes */
409462306a36Sopenharmony_ci#define CTA_DB_AUDIO			1
409562306a36Sopenharmony_ci#define CTA_DB_VIDEO			2
409662306a36Sopenharmony_ci#define CTA_DB_VENDOR			3
409762306a36Sopenharmony_ci#define CTA_DB_SPEAKER			4
409862306a36Sopenharmony_ci#define CTA_DB_EXTENDED_TAG		7
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_ci/* CTA-861-H Table 62 - CTA Extended Tag Codes */
410162306a36Sopenharmony_ci#define CTA_EXT_DB_VIDEO_CAP		0
410262306a36Sopenharmony_ci#define CTA_EXT_DB_VENDOR		1
410362306a36Sopenharmony_ci#define CTA_EXT_DB_HDR_STATIC_METADATA	6
410462306a36Sopenharmony_ci#define CTA_EXT_DB_420_VIDEO_DATA	14
410562306a36Sopenharmony_ci#define CTA_EXT_DB_420_VIDEO_CAP_MAP	15
410662306a36Sopenharmony_ci#define CTA_EXT_DB_HF_EEODB		0x78
410762306a36Sopenharmony_ci#define CTA_EXT_DB_HF_SCDB		0x79
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci#define EDID_BASIC_AUDIO	(1 << 6)
411062306a36Sopenharmony_ci#define EDID_CEA_YCRCB444	(1 << 5)
411162306a36Sopenharmony_ci#define EDID_CEA_YCRCB422	(1 << 4)
411262306a36Sopenharmony_ci#define EDID_CEA_VCDB_QS	(1 << 6)
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci/*
411562306a36Sopenharmony_ci * Search EDID for CEA extension block.
411662306a36Sopenharmony_ci *
411762306a36Sopenharmony_ci * FIXME: Prefer not returning pointers to raw EDID data.
411862306a36Sopenharmony_ci */
411962306a36Sopenharmony_ciconst u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
412062306a36Sopenharmony_ci				  int ext_id, int *ext_index)
412162306a36Sopenharmony_ci{
412262306a36Sopenharmony_ci	const u8 *edid_ext = NULL;
412362306a36Sopenharmony_ci	int i;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	/* No EDID or EDID extensions */
412662306a36Sopenharmony_ci	if (!drm_edid || !drm_edid_extension_block_count(drm_edid))
412762306a36Sopenharmony_ci		return NULL;
412862306a36Sopenharmony_ci
412962306a36Sopenharmony_ci	/* Find CEA extension */
413062306a36Sopenharmony_ci	for (i = *ext_index; i < drm_edid_extension_block_count(drm_edid); i++) {
413162306a36Sopenharmony_ci		edid_ext = drm_edid_extension_block_data(drm_edid, i);
413262306a36Sopenharmony_ci		if (edid_block_tag(edid_ext) == ext_id)
413362306a36Sopenharmony_ci			break;
413462306a36Sopenharmony_ci	}
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_ci	if (i >= drm_edid_extension_block_count(drm_edid))
413762306a36Sopenharmony_ci		return NULL;
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	*ext_index = i + 1;
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	return edid_ext;
414262306a36Sopenharmony_ci}
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci/* Return true if the EDID has a CTA extension or a DisplayID CTA data block */
414562306a36Sopenharmony_cistatic bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid)
414662306a36Sopenharmony_ci{
414762306a36Sopenharmony_ci	const struct displayid_block *block;
414862306a36Sopenharmony_ci	struct displayid_iter iter;
414962306a36Sopenharmony_ci	int ext_index = 0;
415062306a36Sopenharmony_ci	bool found = false;
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci	/* Look for a top level CEA extension block */
415362306a36Sopenharmony_ci	if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index))
415462306a36Sopenharmony_ci		return true;
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	/* CEA blocks can also be found embedded in a DisplayID block */
415762306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter);
415862306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter) {
415962306a36Sopenharmony_ci		if (block->tag == DATA_BLOCK_CTA) {
416062306a36Sopenharmony_ci			found = true;
416162306a36Sopenharmony_ci			break;
416262306a36Sopenharmony_ci		}
416362306a36Sopenharmony_ci	}
416462306a36Sopenharmony_ci	displayid_iter_end(&iter);
416562306a36Sopenharmony_ci
416662306a36Sopenharmony_ci	return found;
416762306a36Sopenharmony_ci}
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_cistatic __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic)
417062306a36Sopenharmony_ci{
417162306a36Sopenharmony_ci	BUILD_BUG_ON(1 + ARRAY_SIZE(edid_cea_modes_1) - 1 != 127);
417262306a36Sopenharmony_ci	BUILD_BUG_ON(193 + ARRAY_SIZE(edid_cea_modes_193) - 1 != 219);
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci	if (vic >= 1 && vic < 1 + ARRAY_SIZE(edid_cea_modes_1))
417562306a36Sopenharmony_ci		return &edid_cea_modes_1[vic - 1];
417662306a36Sopenharmony_ci	if (vic >= 193 && vic < 193 + ARRAY_SIZE(edid_cea_modes_193))
417762306a36Sopenharmony_ci		return &edid_cea_modes_193[vic - 193];
417862306a36Sopenharmony_ci	return NULL;
417962306a36Sopenharmony_ci}
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_cistatic u8 cea_num_vics(void)
418262306a36Sopenharmony_ci{
418362306a36Sopenharmony_ci	return 193 + ARRAY_SIZE(edid_cea_modes_193);
418462306a36Sopenharmony_ci}
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_cistatic u8 cea_next_vic(u8 vic)
418762306a36Sopenharmony_ci{
418862306a36Sopenharmony_ci	if (++vic == 1 + ARRAY_SIZE(edid_cea_modes_1))
418962306a36Sopenharmony_ci		vic = 193;
419062306a36Sopenharmony_ci	return vic;
419162306a36Sopenharmony_ci}
419262306a36Sopenharmony_ci
419362306a36Sopenharmony_ci/*
419462306a36Sopenharmony_ci * Calculate the alternate clock for the CEA mode
419562306a36Sopenharmony_ci * (60Hz vs. 59.94Hz etc.)
419662306a36Sopenharmony_ci */
419762306a36Sopenharmony_cistatic unsigned int
419862306a36Sopenharmony_cicea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
419962306a36Sopenharmony_ci{
420062306a36Sopenharmony_ci	unsigned int clock = cea_mode->clock;
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_ci	if (drm_mode_vrefresh(cea_mode) % 6 != 0)
420362306a36Sopenharmony_ci		return clock;
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	/*
420662306a36Sopenharmony_ci	 * edid_cea_modes contains the 59.94Hz
420762306a36Sopenharmony_ci	 * variant for 240 and 480 line modes,
420862306a36Sopenharmony_ci	 * and the 60Hz variant otherwise.
420962306a36Sopenharmony_ci	 */
421062306a36Sopenharmony_ci	if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
421162306a36Sopenharmony_ci		clock = DIV_ROUND_CLOSEST(clock * 1001, 1000);
421262306a36Sopenharmony_ci	else
421362306a36Sopenharmony_ci		clock = DIV_ROUND_CLOSEST(clock * 1000, 1001);
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci	return clock;
421662306a36Sopenharmony_ci}
421762306a36Sopenharmony_ci
421862306a36Sopenharmony_cistatic bool
421962306a36Sopenharmony_cicea_mode_alternate_timings(u8 vic, struct drm_display_mode *mode)
422062306a36Sopenharmony_ci{
422162306a36Sopenharmony_ci	/*
422262306a36Sopenharmony_ci	 * For certain VICs the spec allows the vertical
422362306a36Sopenharmony_ci	 * front porch to vary by one or two lines.
422462306a36Sopenharmony_ci	 *
422562306a36Sopenharmony_ci	 * cea_modes[] stores the variant with the shortest
422662306a36Sopenharmony_ci	 * vertical front porch. We can adjust the mode to
422762306a36Sopenharmony_ci	 * get the other variants by simply increasing the
422862306a36Sopenharmony_ci	 * vertical front porch length.
422962306a36Sopenharmony_ci	 */
423062306a36Sopenharmony_ci	BUILD_BUG_ON(cea_mode_for_vic(8)->vtotal != 262 ||
423162306a36Sopenharmony_ci		     cea_mode_for_vic(9)->vtotal != 262 ||
423262306a36Sopenharmony_ci		     cea_mode_for_vic(12)->vtotal != 262 ||
423362306a36Sopenharmony_ci		     cea_mode_for_vic(13)->vtotal != 262 ||
423462306a36Sopenharmony_ci		     cea_mode_for_vic(23)->vtotal != 312 ||
423562306a36Sopenharmony_ci		     cea_mode_for_vic(24)->vtotal != 312 ||
423662306a36Sopenharmony_ci		     cea_mode_for_vic(27)->vtotal != 312 ||
423762306a36Sopenharmony_ci		     cea_mode_for_vic(28)->vtotal != 312);
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	if (((vic == 8 || vic == 9 ||
424062306a36Sopenharmony_ci	      vic == 12 || vic == 13) && mode->vtotal < 263) ||
424162306a36Sopenharmony_ci	    ((vic == 23 || vic == 24 ||
424262306a36Sopenharmony_ci	      vic == 27 || vic == 28) && mode->vtotal < 314)) {
424362306a36Sopenharmony_ci		mode->vsync_start++;
424462306a36Sopenharmony_ci		mode->vsync_end++;
424562306a36Sopenharmony_ci		mode->vtotal++;
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci		return true;
424862306a36Sopenharmony_ci	}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci	return false;
425162306a36Sopenharmony_ci}
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_cistatic u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
425462306a36Sopenharmony_ci					     unsigned int clock_tolerance)
425562306a36Sopenharmony_ci{
425662306a36Sopenharmony_ci	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
425762306a36Sopenharmony_ci	u8 vic;
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci	if (!to_match->clock)
426062306a36Sopenharmony_ci		return 0;
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	if (to_match->picture_aspect_ratio)
426362306a36Sopenharmony_ci		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
426662306a36Sopenharmony_ci		struct drm_display_mode cea_mode;
426762306a36Sopenharmony_ci		unsigned int clock1, clock2;
426862306a36Sopenharmony_ci
426962306a36Sopenharmony_ci		drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
427062306a36Sopenharmony_ci
427162306a36Sopenharmony_ci		/* Check both 60Hz and 59.94Hz */
427262306a36Sopenharmony_ci		clock1 = cea_mode.clock;
427362306a36Sopenharmony_ci		clock2 = cea_mode_alternate_clock(&cea_mode);
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci		if (abs(to_match->clock - clock1) > clock_tolerance &&
427662306a36Sopenharmony_ci		    abs(to_match->clock - clock2) > clock_tolerance)
427762306a36Sopenharmony_ci			continue;
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci		do {
428062306a36Sopenharmony_ci			if (drm_mode_match(to_match, &cea_mode, match_flags))
428162306a36Sopenharmony_ci				return vic;
428262306a36Sopenharmony_ci		} while (cea_mode_alternate_timings(vic, &cea_mode));
428362306a36Sopenharmony_ci	}
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	return 0;
428662306a36Sopenharmony_ci}
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_ci/**
428962306a36Sopenharmony_ci * drm_match_cea_mode - look for a CEA mode matching given mode
429062306a36Sopenharmony_ci * @to_match: display mode
429162306a36Sopenharmony_ci *
429262306a36Sopenharmony_ci * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
429362306a36Sopenharmony_ci * mode.
429462306a36Sopenharmony_ci */
429562306a36Sopenharmony_ciu8 drm_match_cea_mode(const struct drm_display_mode *to_match)
429662306a36Sopenharmony_ci{
429762306a36Sopenharmony_ci	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
429862306a36Sopenharmony_ci	u8 vic;
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_ci	if (!to_match->clock)
430162306a36Sopenharmony_ci		return 0;
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_ci	if (to_match->picture_aspect_ratio)
430462306a36Sopenharmony_ci		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_ci	for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
430762306a36Sopenharmony_ci		struct drm_display_mode cea_mode;
430862306a36Sopenharmony_ci		unsigned int clock1, clock2;
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci		drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci		/* Check both 60Hz and 59.94Hz */
431362306a36Sopenharmony_ci		clock1 = cea_mode.clock;
431462306a36Sopenharmony_ci		clock2 = cea_mode_alternate_clock(&cea_mode);
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci		if (KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock1) &&
431762306a36Sopenharmony_ci		    KHZ2PICOS(to_match->clock) != KHZ2PICOS(clock2))
431862306a36Sopenharmony_ci			continue;
431962306a36Sopenharmony_ci
432062306a36Sopenharmony_ci		do {
432162306a36Sopenharmony_ci			if (drm_mode_match(to_match, &cea_mode, match_flags))
432262306a36Sopenharmony_ci				return vic;
432362306a36Sopenharmony_ci		} while (cea_mode_alternate_timings(vic, &cea_mode));
432462306a36Sopenharmony_ci	}
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ci	return 0;
432762306a36Sopenharmony_ci}
432862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_match_cea_mode);
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_cistatic bool drm_valid_cea_vic(u8 vic)
433162306a36Sopenharmony_ci{
433262306a36Sopenharmony_ci	return cea_mode_for_vic(vic) != NULL;
433362306a36Sopenharmony_ci}
433462306a36Sopenharmony_ci
433562306a36Sopenharmony_cistatic enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
433662306a36Sopenharmony_ci{
433762306a36Sopenharmony_ci	const struct drm_display_mode *mode = cea_mode_for_vic(video_code);
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	if (mode)
434062306a36Sopenharmony_ci		return mode->picture_aspect_ratio;
434162306a36Sopenharmony_ci
434262306a36Sopenharmony_ci	return HDMI_PICTURE_ASPECT_NONE;
434362306a36Sopenharmony_ci}
434462306a36Sopenharmony_ci
434562306a36Sopenharmony_cistatic enum hdmi_picture_aspect drm_get_hdmi_aspect_ratio(const u8 video_code)
434662306a36Sopenharmony_ci{
434762306a36Sopenharmony_ci	return edid_4k_modes[video_code].picture_aspect_ratio;
434862306a36Sopenharmony_ci}
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci/*
435162306a36Sopenharmony_ci * Calculate the alternate clock for HDMI modes (those from the HDMI vendor
435262306a36Sopenharmony_ci * specific block).
435362306a36Sopenharmony_ci */
435462306a36Sopenharmony_cistatic unsigned int
435562306a36Sopenharmony_cihdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
435662306a36Sopenharmony_ci{
435762306a36Sopenharmony_ci	return cea_mode_alternate_clock(hdmi_mode);
435862306a36Sopenharmony_ci}
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_cistatic u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
436162306a36Sopenharmony_ci					      unsigned int clock_tolerance)
436262306a36Sopenharmony_ci{
436362306a36Sopenharmony_ci	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
436462306a36Sopenharmony_ci	u8 vic;
436562306a36Sopenharmony_ci
436662306a36Sopenharmony_ci	if (!to_match->clock)
436762306a36Sopenharmony_ci		return 0;
436862306a36Sopenharmony_ci
436962306a36Sopenharmony_ci	if (to_match->picture_aspect_ratio)
437062306a36Sopenharmony_ci		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
437362306a36Sopenharmony_ci		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
437462306a36Sopenharmony_ci		unsigned int clock1, clock2;
437562306a36Sopenharmony_ci
437662306a36Sopenharmony_ci		/* Make sure to also match alternate clocks */
437762306a36Sopenharmony_ci		clock1 = hdmi_mode->clock;
437862306a36Sopenharmony_ci		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
437962306a36Sopenharmony_ci
438062306a36Sopenharmony_ci		if (abs(to_match->clock - clock1) > clock_tolerance &&
438162306a36Sopenharmony_ci		    abs(to_match->clock - clock2) > clock_tolerance)
438262306a36Sopenharmony_ci			continue;
438362306a36Sopenharmony_ci
438462306a36Sopenharmony_ci		if (drm_mode_match(to_match, hdmi_mode, match_flags))
438562306a36Sopenharmony_ci			return vic;
438662306a36Sopenharmony_ci	}
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	return 0;
438962306a36Sopenharmony_ci}
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_ci/*
439262306a36Sopenharmony_ci * drm_match_hdmi_mode - look for a HDMI mode matching given mode
439362306a36Sopenharmony_ci * @to_match: display mode
439462306a36Sopenharmony_ci *
439562306a36Sopenharmony_ci * An HDMI mode is one defined in the HDMI vendor specific block.
439662306a36Sopenharmony_ci *
439762306a36Sopenharmony_ci * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one.
439862306a36Sopenharmony_ci */
439962306a36Sopenharmony_cistatic u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
440062306a36Sopenharmony_ci{
440162306a36Sopenharmony_ci	unsigned int match_flags = DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS;
440262306a36Sopenharmony_ci	u8 vic;
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci	if (!to_match->clock)
440562306a36Sopenharmony_ci		return 0;
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	if (to_match->picture_aspect_ratio)
440862306a36Sopenharmony_ci		match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
440962306a36Sopenharmony_ci
441062306a36Sopenharmony_ci	for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
441162306a36Sopenharmony_ci		const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
441262306a36Sopenharmony_ci		unsigned int clock1, clock2;
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci		/* Make sure to also match alternate clocks */
441562306a36Sopenharmony_ci		clock1 = hdmi_mode->clock;
441662306a36Sopenharmony_ci		clock2 = hdmi_mode_alternate_clock(hdmi_mode);
441762306a36Sopenharmony_ci
441862306a36Sopenharmony_ci		if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
441962306a36Sopenharmony_ci		     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
442062306a36Sopenharmony_ci		    drm_mode_match(to_match, hdmi_mode, match_flags))
442162306a36Sopenharmony_ci			return vic;
442262306a36Sopenharmony_ci	}
442362306a36Sopenharmony_ci	return 0;
442462306a36Sopenharmony_ci}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_cistatic bool drm_valid_hdmi_vic(u8 vic)
442762306a36Sopenharmony_ci{
442862306a36Sopenharmony_ci	return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
442962306a36Sopenharmony_ci}
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_cistatic int add_alternate_cea_modes(struct drm_connector *connector,
443262306a36Sopenharmony_ci				   const struct drm_edid *drm_edid)
443362306a36Sopenharmony_ci{
443462306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
443562306a36Sopenharmony_ci	struct drm_display_mode *mode, *tmp;
443662306a36Sopenharmony_ci	LIST_HEAD(list);
443762306a36Sopenharmony_ci	int modes = 0;
443862306a36Sopenharmony_ci
443962306a36Sopenharmony_ci	/* Don't add CTA modes if the CTA extension block is missing */
444062306a36Sopenharmony_ci	if (!drm_edid_has_cta_extension(drm_edid))
444162306a36Sopenharmony_ci		return 0;
444262306a36Sopenharmony_ci
444362306a36Sopenharmony_ci	/*
444462306a36Sopenharmony_ci	 * Go through all probed modes and create a new mode
444562306a36Sopenharmony_ci	 * with the alternate clock for certain CEA modes.
444662306a36Sopenharmony_ci	 */
444762306a36Sopenharmony_ci	list_for_each_entry(mode, &connector->probed_modes, head) {
444862306a36Sopenharmony_ci		const struct drm_display_mode *cea_mode = NULL;
444962306a36Sopenharmony_ci		struct drm_display_mode *newmode;
445062306a36Sopenharmony_ci		u8 vic = drm_match_cea_mode(mode);
445162306a36Sopenharmony_ci		unsigned int clock1, clock2;
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci		if (drm_valid_cea_vic(vic)) {
445462306a36Sopenharmony_ci			cea_mode = cea_mode_for_vic(vic);
445562306a36Sopenharmony_ci			clock2 = cea_mode_alternate_clock(cea_mode);
445662306a36Sopenharmony_ci		} else {
445762306a36Sopenharmony_ci			vic = drm_match_hdmi_mode(mode);
445862306a36Sopenharmony_ci			if (drm_valid_hdmi_vic(vic)) {
445962306a36Sopenharmony_ci				cea_mode = &edid_4k_modes[vic];
446062306a36Sopenharmony_ci				clock2 = hdmi_mode_alternate_clock(cea_mode);
446162306a36Sopenharmony_ci			}
446262306a36Sopenharmony_ci		}
446362306a36Sopenharmony_ci
446462306a36Sopenharmony_ci		if (!cea_mode)
446562306a36Sopenharmony_ci			continue;
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci		clock1 = cea_mode->clock;
446862306a36Sopenharmony_ci
446962306a36Sopenharmony_ci		if (clock1 == clock2)
447062306a36Sopenharmony_ci			continue;
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci		if (mode->clock != clock1 && mode->clock != clock2)
447362306a36Sopenharmony_ci			continue;
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci		newmode = drm_mode_duplicate(dev, cea_mode);
447662306a36Sopenharmony_ci		if (!newmode)
447762306a36Sopenharmony_ci			continue;
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci		/* Carry over the stereo flags */
448062306a36Sopenharmony_ci		newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK;
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci		/*
448362306a36Sopenharmony_ci		 * The current mode could be either variant. Make
448462306a36Sopenharmony_ci		 * sure to pick the "other" clock for the new mode.
448562306a36Sopenharmony_ci		 */
448662306a36Sopenharmony_ci		if (mode->clock != clock1)
448762306a36Sopenharmony_ci			newmode->clock = clock1;
448862306a36Sopenharmony_ci		else
448962306a36Sopenharmony_ci			newmode->clock = clock2;
449062306a36Sopenharmony_ci
449162306a36Sopenharmony_ci		list_add_tail(&newmode->head, &list);
449262306a36Sopenharmony_ci	}
449362306a36Sopenharmony_ci
449462306a36Sopenharmony_ci	list_for_each_entry_safe(mode, tmp, &list, head) {
449562306a36Sopenharmony_ci		list_del(&mode->head);
449662306a36Sopenharmony_ci		drm_mode_probed_add(connector, mode);
449762306a36Sopenharmony_ci		modes++;
449862306a36Sopenharmony_ci	}
449962306a36Sopenharmony_ci
450062306a36Sopenharmony_ci	return modes;
450162306a36Sopenharmony_ci}
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_cistatic u8 svd_to_vic(u8 svd)
450462306a36Sopenharmony_ci{
450562306a36Sopenharmony_ci	/* 0-6 bit vic, 7th bit native mode indicator */
450662306a36Sopenharmony_ci	if ((svd >= 1 &&  svd <= 64) || (svd >= 129 && svd <= 192))
450762306a36Sopenharmony_ci		return svd & 127;
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci	return svd;
451062306a36Sopenharmony_ci}
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_ci/*
451362306a36Sopenharmony_ci * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in
451462306a36Sopenharmony_ci * the EDID, or NULL on errors.
451562306a36Sopenharmony_ci */
451662306a36Sopenharmony_cistatic struct drm_display_mode *
451762306a36Sopenharmony_cidrm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index)
451862306a36Sopenharmony_ci{
451962306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
452062306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index])
452362306a36Sopenharmony_ci		return NULL;
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci	return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]);
452662306a36Sopenharmony_ci}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci/*
452962306a36Sopenharmony_ci * do_y420vdb_modes - Parse YCBCR 420 only modes
453062306a36Sopenharmony_ci * @connector: connector corresponding to the HDMI sink
453162306a36Sopenharmony_ci * @svds: start of the data block of CEA YCBCR 420 VDB
453262306a36Sopenharmony_ci * @len: length of the CEA YCBCR 420 VDB
453362306a36Sopenharmony_ci *
453462306a36Sopenharmony_ci * Parse the CEA-861-F YCBCR 420 Video Data Block (Y420VDB)
453562306a36Sopenharmony_ci * which contains modes which can be supported in YCBCR 420
453662306a36Sopenharmony_ci * output format only.
453762306a36Sopenharmony_ci */
453862306a36Sopenharmony_cistatic int do_y420vdb_modes(struct drm_connector *connector,
453962306a36Sopenharmony_ci			    const u8 *svds, u8 svds_len)
454062306a36Sopenharmony_ci{
454162306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
454262306a36Sopenharmony_ci	int modes = 0, i;
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci	for (i = 0; i < svds_len; i++) {
454562306a36Sopenharmony_ci		u8 vic = svd_to_vic(svds[i]);
454662306a36Sopenharmony_ci		struct drm_display_mode *newmode;
454762306a36Sopenharmony_ci
454862306a36Sopenharmony_ci		if (!drm_valid_cea_vic(vic))
454962306a36Sopenharmony_ci			continue;
455062306a36Sopenharmony_ci
455162306a36Sopenharmony_ci		newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic));
455262306a36Sopenharmony_ci		if (!newmode)
455362306a36Sopenharmony_ci			break;
455462306a36Sopenharmony_ci		drm_mode_probed_add(connector, newmode);
455562306a36Sopenharmony_ci		modes++;
455662306a36Sopenharmony_ci	}
455762306a36Sopenharmony_ci
455862306a36Sopenharmony_ci	return modes;
455962306a36Sopenharmony_ci}
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci/**
456262306a36Sopenharmony_ci * drm_display_mode_from_cea_vic() - return a mode for CEA VIC
456362306a36Sopenharmony_ci * @dev: DRM device
456462306a36Sopenharmony_ci * @video_code: CEA VIC of the mode
456562306a36Sopenharmony_ci *
456662306a36Sopenharmony_ci * Creates a new mode matching the specified CEA VIC.
456762306a36Sopenharmony_ci *
456862306a36Sopenharmony_ci * Returns: A new drm_display_mode on success or NULL on failure
456962306a36Sopenharmony_ci */
457062306a36Sopenharmony_cistruct drm_display_mode *
457162306a36Sopenharmony_cidrm_display_mode_from_cea_vic(struct drm_device *dev,
457262306a36Sopenharmony_ci			      u8 video_code)
457362306a36Sopenharmony_ci{
457462306a36Sopenharmony_ci	const struct drm_display_mode *cea_mode;
457562306a36Sopenharmony_ci	struct drm_display_mode *newmode;
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_ci	cea_mode = cea_mode_for_vic(video_code);
457862306a36Sopenharmony_ci	if (!cea_mode)
457962306a36Sopenharmony_ci		return NULL;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci	newmode = drm_mode_duplicate(dev, cea_mode);
458262306a36Sopenharmony_ci	if (!newmode)
458362306a36Sopenharmony_ci		return NULL;
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci	return newmode;
458662306a36Sopenharmony_ci}
458762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_display_mode_from_cea_vic);
458862306a36Sopenharmony_ci
458962306a36Sopenharmony_ci/* Add modes based on VICs parsed in parse_cta_vdb() */
459062306a36Sopenharmony_cistatic int add_cta_vdb_modes(struct drm_connector *connector)
459162306a36Sopenharmony_ci{
459262306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
459362306a36Sopenharmony_ci	int i, modes = 0;
459462306a36Sopenharmony_ci
459562306a36Sopenharmony_ci	if (!info->vics)
459662306a36Sopenharmony_ci		return 0;
459762306a36Sopenharmony_ci
459862306a36Sopenharmony_ci	for (i = 0; i < info->vics_len; i++) {
459962306a36Sopenharmony_ci		struct drm_display_mode *mode;
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci		mode = drm_display_mode_from_vic_index(connector, i);
460262306a36Sopenharmony_ci		if (mode) {
460362306a36Sopenharmony_ci			drm_mode_probed_add(connector, mode);
460462306a36Sopenharmony_ci			modes++;
460562306a36Sopenharmony_ci		}
460662306a36Sopenharmony_ci	}
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	return modes;
460962306a36Sopenharmony_ci}
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_cistruct stereo_mandatory_mode {
461262306a36Sopenharmony_ci	int width, height, vrefresh;
461362306a36Sopenharmony_ci	unsigned int flags;
461462306a36Sopenharmony_ci};
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_cistatic const struct stereo_mandatory_mode stereo_mandatory_modes[] = {
461762306a36Sopenharmony_ci	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
461862306a36Sopenharmony_ci	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING },
461962306a36Sopenharmony_ci	{ 1920, 1080, 50,
462062306a36Sopenharmony_ci	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
462162306a36Sopenharmony_ci	{ 1920, 1080, 60,
462262306a36Sopenharmony_ci	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
462362306a36Sopenharmony_ci	{ 1280, 720,  50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
462462306a36Sopenharmony_ci	{ 1280, 720,  50, DRM_MODE_FLAG_3D_FRAME_PACKING },
462562306a36Sopenharmony_ci	{ 1280, 720,  60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM },
462662306a36Sopenharmony_ci	{ 1280, 720,  60, DRM_MODE_FLAG_3D_FRAME_PACKING }
462762306a36Sopenharmony_ci};
462862306a36Sopenharmony_ci
462962306a36Sopenharmony_cistatic bool
463062306a36Sopenharmony_cistereo_match_mandatory(const struct drm_display_mode *mode,
463162306a36Sopenharmony_ci		       const struct stereo_mandatory_mode *stereo_mode)
463262306a36Sopenharmony_ci{
463362306a36Sopenharmony_ci	unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci	return mode->hdisplay == stereo_mode->width &&
463662306a36Sopenharmony_ci	       mode->vdisplay == stereo_mode->height &&
463762306a36Sopenharmony_ci	       interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) &&
463862306a36Sopenharmony_ci	       drm_mode_vrefresh(mode) == stereo_mode->vrefresh;
463962306a36Sopenharmony_ci}
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_cistatic int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector)
464262306a36Sopenharmony_ci{
464362306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
464462306a36Sopenharmony_ci	const struct drm_display_mode *mode;
464562306a36Sopenharmony_ci	struct list_head stereo_modes;
464662306a36Sopenharmony_ci	int modes = 0, i;
464762306a36Sopenharmony_ci
464862306a36Sopenharmony_ci	INIT_LIST_HEAD(&stereo_modes);
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	list_for_each_entry(mode, &connector->probed_modes, head) {
465162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) {
465262306a36Sopenharmony_ci			const struct stereo_mandatory_mode *mandatory;
465362306a36Sopenharmony_ci			struct drm_display_mode *new_mode;
465462306a36Sopenharmony_ci
465562306a36Sopenharmony_ci			if (!stereo_match_mandatory(mode,
465662306a36Sopenharmony_ci						    &stereo_mandatory_modes[i]))
465762306a36Sopenharmony_ci				continue;
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci			mandatory = &stereo_mandatory_modes[i];
466062306a36Sopenharmony_ci			new_mode = drm_mode_duplicate(dev, mode);
466162306a36Sopenharmony_ci			if (!new_mode)
466262306a36Sopenharmony_ci				continue;
466362306a36Sopenharmony_ci
466462306a36Sopenharmony_ci			new_mode->flags |= mandatory->flags;
466562306a36Sopenharmony_ci			list_add_tail(&new_mode->head, &stereo_modes);
466662306a36Sopenharmony_ci			modes++;
466762306a36Sopenharmony_ci		}
466862306a36Sopenharmony_ci	}
466962306a36Sopenharmony_ci
467062306a36Sopenharmony_ci	list_splice_tail(&stereo_modes, &connector->probed_modes);
467162306a36Sopenharmony_ci
467262306a36Sopenharmony_ci	return modes;
467362306a36Sopenharmony_ci}
467462306a36Sopenharmony_ci
467562306a36Sopenharmony_cistatic int add_hdmi_mode(struct drm_connector *connector, u8 vic)
467662306a36Sopenharmony_ci{
467762306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
467862306a36Sopenharmony_ci	struct drm_display_mode *newmode;
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	if (!drm_valid_hdmi_vic(vic)) {
468162306a36Sopenharmony_ci		drm_err(connector->dev, "[CONNECTOR:%d:%s] Unknown HDMI VIC: %d\n",
468262306a36Sopenharmony_ci			connector->base.id, connector->name, vic);
468362306a36Sopenharmony_ci		return 0;
468462306a36Sopenharmony_ci	}
468562306a36Sopenharmony_ci
468662306a36Sopenharmony_ci	newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]);
468762306a36Sopenharmony_ci	if (!newmode)
468862306a36Sopenharmony_ci		return 0;
468962306a36Sopenharmony_ci
469062306a36Sopenharmony_ci	drm_mode_probed_add(connector, newmode);
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_ci	return 1;
469362306a36Sopenharmony_ci}
469462306a36Sopenharmony_ci
469562306a36Sopenharmony_cistatic int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
469662306a36Sopenharmony_ci			       int vic_index)
469762306a36Sopenharmony_ci{
469862306a36Sopenharmony_ci	struct drm_display_mode *newmode;
469962306a36Sopenharmony_ci	int modes = 0;
470062306a36Sopenharmony_ci
470162306a36Sopenharmony_ci	if (structure & (1 << 0)) {
470262306a36Sopenharmony_ci		newmode = drm_display_mode_from_vic_index(connector, vic_index);
470362306a36Sopenharmony_ci		if (newmode) {
470462306a36Sopenharmony_ci			newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
470562306a36Sopenharmony_ci			drm_mode_probed_add(connector, newmode);
470662306a36Sopenharmony_ci			modes++;
470762306a36Sopenharmony_ci		}
470862306a36Sopenharmony_ci	}
470962306a36Sopenharmony_ci	if (structure & (1 << 6)) {
471062306a36Sopenharmony_ci		newmode = drm_display_mode_from_vic_index(connector, vic_index);
471162306a36Sopenharmony_ci		if (newmode) {
471262306a36Sopenharmony_ci			newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
471362306a36Sopenharmony_ci			drm_mode_probed_add(connector, newmode);
471462306a36Sopenharmony_ci			modes++;
471562306a36Sopenharmony_ci		}
471662306a36Sopenharmony_ci	}
471762306a36Sopenharmony_ci	if (structure & (1 << 8)) {
471862306a36Sopenharmony_ci		newmode = drm_display_mode_from_vic_index(connector, vic_index);
471962306a36Sopenharmony_ci		if (newmode) {
472062306a36Sopenharmony_ci			newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
472162306a36Sopenharmony_ci			drm_mode_probed_add(connector, newmode);
472262306a36Sopenharmony_ci			modes++;
472362306a36Sopenharmony_ci		}
472462306a36Sopenharmony_ci	}
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_ci	return modes;
472762306a36Sopenharmony_ci}
472862306a36Sopenharmony_ci
472962306a36Sopenharmony_cistatic bool hdmi_vsdb_latency_present(const u8 *db)
473062306a36Sopenharmony_ci{
473162306a36Sopenharmony_ci	return db[8] & BIT(7);
473262306a36Sopenharmony_ci}
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_cistatic bool hdmi_vsdb_i_latency_present(const u8 *db)
473562306a36Sopenharmony_ci{
473662306a36Sopenharmony_ci	return hdmi_vsdb_latency_present(db) && db[8] & BIT(6);
473762306a36Sopenharmony_ci}
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_cistatic int hdmi_vsdb_latency_length(const u8 *db)
474062306a36Sopenharmony_ci{
474162306a36Sopenharmony_ci	if (hdmi_vsdb_i_latency_present(db))
474262306a36Sopenharmony_ci		return 4;
474362306a36Sopenharmony_ci	else if (hdmi_vsdb_latency_present(db))
474462306a36Sopenharmony_ci		return 2;
474562306a36Sopenharmony_ci	else
474662306a36Sopenharmony_ci		return 0;
474762306a36Sopenharmony_ci}
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_ci/*
475062306a36Sopenharmony_ci * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
475162306a36Sopenharmony_ci * @connector: connector corresponding to the HDMI sink
475262306a36Sopenharmony_ci * @db: start of the CEA vendor specific block
475362306a36Sopenharmony_ci * @len: length of the CEA block payload, ie. one can access up to db[len]
475462306a36Sopenharmony_ci *
475562306a36Sopenharmony_ci * Parses the HDMI VSDB looking for modes to add to @connector. This function
475662306a36Sopenharmony_ci * also adds the stereo 3d modes when applicable.
475762306a36Sopenharmony_ci */
475862306a36Sopenharmony_cistatic int
475962306a36Sopenharmony_cido_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
476062306a36Sopenharmony_ci{
476162306a36Sopenharmony_ci	int modes = 0, offset = 0, i, multi_present = 0, multi_len;
476262306a36Sopenharmony_ci	u8 vic_len, hdmi_3d_len = 0;
476362306a36Sopenharmony_ci	u16 mask;
476462306a36Sopenharmony_ci	u16 structure_all;
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci	if (len < 8)
476762306a36Sopenharmony_ci		goto out;
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	/* no HDMI_Video_Present */
477062306a36Sopenharmony_ci	if (!(db[8] & (1 << 5)))
477162306a36Sopenharmony_ci		goto out;
477262306a36Sopenharmony_ci
477362306a36Sopenharmony_ci	offset += hdmi_vsdb_latency_length(db);
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	/* the declared length is not long enough for the 2 first bytes
477662306a36Sopenharmony_ci	 * of additional video format capabilities */
477762306a36Sopenharmony_ci	if (len < (8 + offset + 2))
477862306a36Sopenharmony_ci		goto out;
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci	/* 3D_Present */
478162306a36Sopenharmony_ci	offset++;
478262306a36Sopenharmony_ci	if (db[8 + offset] & (1 << 7)) {
478362306a36Sopenharmony_ci		modes += add_hdmi_mandatory_stereo_modes(connector);
478462306a36Sopenharmony_ci
478562306a36Sopenharmony_ci		/* 3D_Multi_present */
478662306a36Sopenharmony_ci		multi_present = (db[8 + offset] & 0x60) >> 5;
478762306a36Sopenharmony_ci	}
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci	offset++;
479062306a36Sopenharmony_ci	vic_len = db[8 + offset] >> 5;
479162306a36Sopenharmony_ci	hdmi_3d_len = db[8 + offset] & 0x1f;
479262306a36Sopenharmony_ci
479362306a36Sopenharmony_ci	for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
479462306a36Sopenharmony_ci		u8 vic;
479562306a36Sopenharmony_ci
479662306a36Sopenharmony_ci		vic = db[9 + offset + i];
479762306a36Sopenharmony_ci		modes += add_hdmi_mode(connector, vic);
479862306a36Sopenharmony_ci	}
479962306a36Sopenharmony_ci	offset += 1 + vic_len;
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_ci	if (multi_present == 1)
480262306a36Sopenharmony_ci		multi_len = 2;
480362306a36Sopenharmony_ci	else if (multi_present == 2)
480462306a36Sopenharmony_ci		multi_len = 4;
480562306a36Sopenharmony_ci	else
480662306a36Sopenharmony_ci		multi_len = 0;
480762306a36Sopenharmony_ci
480862306a36Sopenharmony_ci	if (len < (8 + offset + hdmi_3d_len - 1))
480962306a36Sopenharmony_ci		goto out;
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	if (hdmi_3d_len < multi_len)
481262306a36Sopenharmony_ci		goto out;
481362306a36Sopenharmony_ci
481462306a36Sopenharmony_ci	if (multi_present == 1 || multi_present == 2) {
481562306a36Sopenharmony_ci		/* 3D_Structure_ALL */
481662306a36Sopenharmony_ci		structure_all = (db[8 + offset] << 8) | db[9 + offset];
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_ci		/* check if 3D_MASK is present */
481962306a36Sopenharmony_ci		if (multi_present == 2)
482062306a36Sopenharmony_ci			mask = (db[10 + offset] << 8) | db[11 + offset];
482162306a36Sopenharmony_ci		else
482262306a36Sopenharmony_ci			mask = 0xffff;
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ci		for (i = 0; i < 16; i++) {
482562306a36Sopenharmony_ci			if (mask & (1 << i))
482662306a36Sopenharmony_ci				modes += add_3d_struct_modes(connector,
482762306a36Sopenharmony_ci							     structure_all, i);
482862306a36Sopenharmony_ci		}
482962306a36Sopenharmony_ci	}
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	offset += multi_len;
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	for (i = 0; i < (hdmi_3d_len - multi_len); i++) {
483462306a36Sopenharmony_ci		int vic_index;
483562306a36Sopenharmony_ci		struct drm_display_mode *newmode = NULL;
483662306a36Sopenharmony_ci		unsigned int newflag = 0;
483762306a36Sopenharmony_ci		bool detail_present;
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci		detail_present = ((db[8 + offset + i] & 0x0f) > 7);
484062306a36Sopenharmony_ci
484162306a36Sopenharmony_ci		if (detail_present && (i + 1 == hdmi_3d_len - multi_len))
484262306a36Sopenharmony_ci			break;
484362306a36Sopenharmony_ci
484462306a36Sopenharmony_ci		/* 2D_VIC_order_X */
484562306a36Sopenharmony_ci		vic_index = db[8 + offset + i] >> 4;
484662306a36Sopenharmony_ci
484762306a36Sopenharmony_ci		/* 3D_Structure_X */
484862306a36Sopenharmony_ci		switch (db[8 + offset + i] & 0x0f) {
484962306a36Sopenharmony_ci		case 0:
485062306a36Sopenharmony_ci			newflag = DRM_MODE_FLAG_3D_FRAME_PACKING;
485162306a36Sopenharmony_ci			break;
485262306a36Sopenharmony_ci		case 6:
485362306a36Sopenharmony_ci			newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
485462306a36Sopenharmony_ci			break;
485562306a36Sopenharmony_ci		case 8:
485662306a36Sopenharmony_ci			/* 3D_Detail_X */
485762306a36Sopenharmony_ci			if ((db[9 + offset + i] >> 4) == 1)
485862306a36Sopenharmony_ci				newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
485962306a36Sopenharmony_ci			break;
486062306a36Sopenharmony_ci		}
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci		if (newflag != 0) {
486362306a36Sopenharmony_ci			newmode = drm_display_mode_from_vic_index(connector,
486462306a36Sopenharmony_ci								  vic_index);
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci			if (newmode) {
486762306a36Sopenharmony_ci				newmode->flags |= newflag;
486862306a36Sopenharmony_ci				drm_mode_probed_add(connector, newmode);
486962306a36Sopenharmony_ci				modes++;
487062306a36Sopenharmony_ci			}
487162306a36Sopenharmony_ci		}
487262306a36Sopenharmony_ci
487362306a36Sopenharmony_ci		if (detail_present)
487462306a36Sopenharmony_ci			i++;
487562306a36Sopenharmony_ci	}
487662306a36Sopenharmony_ci
487762306a36Sopenharmony_ciout:
487862306a36Sopenharmony_ci	return modes;
487962306a36Sopenharmony_ci}
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_cistatic int
488262306a36Sopenharmony_cicea_revision(const u8 *cea)
488362306a36Sopenharmony_ci{
488462306a36Sopenharmony_ci	/*
488562306a36Sopenharmony_ci	 * FIXME is this correct for the DispID variant?
488662306a36Sopenharmony_ci	 * The DispID spec doesn't really specify whether
488762306a36Sopenharmony_ci	 * this is the revision of the CEA extension or
488862306a36Sopenharmony_ci	 * the DispID CEA data block. And the only value
488962306a36Sopenharmony_ci	 * given as an example is 0.
489062306a36Sopenharmony_ci	 */
489162306a36Sopenharmony_ci	return cea[1];
489262306a36Sopenharmony_ci}
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_ci/*
489562306a36Sopenharmony_ci * CTA Data Block iterator.
489662306a36Sopenharmony_ci *
489762306a36Sopenharmony_ci * Iterate through all CTA Data Blocks in both EDID CTA Extensions and DisplayID
489862306a36Sopenharmony_ci * CTA Data Blocks.
489962306a36Sopenharmony_ci *
490062306a36Sopenharmony_ci * struct cea_db *db:
490162306a36Sopenharmony_ci * struct cea_db_iter iter;
490262306a36Sopenharmony_ci *
490362306a36Sopenharmony_ci * cea_db_iter_edid_begin(edid, &iter);
490462306a36Sopenharmony_ci * cea_db_iter_for_each(db, &iter) {
490562306a36Sopenharmony_ci *         // do stuff with db
490662306a36Sopenharmony_ci * }
490762306a36Sopenharmony_ci * cea_db_iter_end(&iter);
490862306a36Sopenharmony_ci */
490962306a36Sopenharmony_cistruct cea_db_iter {
491062306a36Sopenharmony_ci	struct drm_edid_iter edid_iter;
491162306a36Sopenharmony_ci	struct displayid_iter displayid_iter;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	/* Current Data Block Collection. */
491462306a36Sopenharmony_ci	const u8 *collection;
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	/* Current Data Block index in current collection. */
491762306a36Sopenharmony_ci	int index;
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	/* End index in current collection. */
492062306a36Sopenharmony_ci	int end;
492162306a36Sopenharmony_ci};
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_ci/* CTA-861-H section 7.4 CTA Data BLock Collection */
492462306a36Sopenharmony_cistruct cea_db {
492562306a36Sopenharmony_ci	u8 tag_length;
492662306a36Sopenharmony_ci	u8 data[];
492762306a36Sopenharmony_ci} __packed;
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_cistatic int cea_db_tag(const struct cea_db *db)
493062306a36Sopenharmony_ci{
493162306a36Sopenharmony_ci	return db->tag_length >> 5;
493262306a36Sopenharmony_ci}
493362306a36Sopenharmony_ci
493462306a36Sopenharmony_cistatic int cea_db_payload_len(const void *_db)
493562306a36Sopenharmony_ci{
493662306a36Sopenharmony_ci	/* FIXME: Transition to passing struct cea_db * everywhere. */
493762306a36Sopenharmony_ci	const struct cea_db *db = _db;
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	return db->tag_length & 0x1f;
494062306a36Sopenharmony_ci}
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_cistatic const void *cea_db_data(const struct cea_db *db)
494362306a36Sopenharmony_ci{
494462306a36Sopenharmony_ci	return db->data;
494562306a36Sopenharmony_ci}
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_cistatic bool cea_db_is_extended_tag(const struct cea_db *db, int tag)
494862306a36Sopenharmony_ci{
494962306a36Sopenharmony_ci	return cea_db_tag(db) == CTA_DB_EXTENDED_TAG &&
495062306a36Sopenharmony_ci		cea_db_payload_len(db) >= 1 &&
495162306a36Sopenharmony_ci		db->data[0] == tag;
495262306a36Sopenharmony_ci}
495362306a36Sopenharmony_ci
495462306a36Sopenharmony_cistatic bool cea_db_is_vendor(const struct cea_db *db, int vendor_oui)
495562306a36Sopenharmony_ci{
495662306a36Sopenharmony_ci	const u8 *data = cea_db_data(db);
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci	return cea_db_tag(db) == CTA_DB_VENDOR &&
495962306a36Sopenharmony_ci		cea_db_payload_len(db) >= 3 &&
496062306a36Sopenharmony_ci		oui(data[2], data[1], data[0]) == vendor_oui;
496162306a36Sopenharmony_ci}
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_cistatic void cea_db_iter_edid_begin(const struct drm_edid *drm_edid,
496462306a36Sopenharmony_ci				   struct cea_db_iter *iter)
496562306a36Sopenharmony_ci{
496662306a36Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	drm_edid_iter_begin(drm_edid, &iter->edid_iter);
496962306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter->displayid_iter);
497062306a36Sopenharmony_ci}
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_cistatic const struct cea_db *
497362306a36Sopenharmony_ci__cea_db_iter_current_block(const struct cea_db_iter *iter)
497462306a36Sopenharmony_ci{
497562306a36Sopenharmony_ci	const struct cea_db *db;
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci	if (!iter->collection)
497862306a36Sopenharmony_ci		return NULL;
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	db = (const struct cea_db *)&iter->collection[iter->index];
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci	if (iter->index + sizeof(*db) <= iter->end &&
498362306a36Sopenharmony_ci	    iter->index + sizeof(*db) + cea_db_payload_len(db) <= iter->end)
498462306a36Sopenharmony_ci		return db;
498562306a36Sopenharmony_ci
498662306a36Sopenharmony_ci	return NULL;
498762306a36Sopenharmony_ci}
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_ci/*
499062306a36Sopenharmony_ci * References:
499162306a36Sopenharmony_ci * - CTA-861-H section 7.3.3 CTA Extension Version 3
499262306a36Sopenharmony_ci */
499362306a36Sopenharmony_cistatic int cea_db_collection_size(const u8 *cta)
499462306a36Sopenharmony_ci{
499562306a36Sopenharmony_ci	u8 d = cta[2];
499662306a36Sopenharmony_ci
499762306a36Sopenharmony_ci	if (d < 4 || d > 127)
499862306a36Sopenharmony_ci		return 0;
499962306a36Sopenharmony_ci
500062306a36Sopenharmony_ci	return d - 4;
500162306a36Sopenharmony_ci}
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_ci/*
500462306a36Sopenharmony_ci * References:
500562306a36Sopenharmony_ci * - VESA E-EDID v1.4
500662306a36Sopenharmony_ci * - CTA-861-H section 7.3.3 CTA Extension Version 3
500762306a36Sopenharmony_ci */
500862306a36Sopenharmony_cistatic const void *__cea_db_iter_edid_next(struct cea_db_iter *iter)
500962306a36Sopenharmony_ci{
501062306a36Sopenharmony_ci	const u8 *ext;
501162306a36Sopenharmony_ci
501262306a36Sopenharmony_ci	drm_edid_iter_for_each(ext, &iter->edid_iter) {
501362306a36Sopenharmony_ci		int size;
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci		/* Only support CTA Extension revision 3+ */
501662306a36Sopenharmony_ci		if (ext[0] != CEA_EXT || cea_revision(ext) < 3)
501762306a36Sopenharmony_ci			continue;
501862306a36Sopenharmony_ci
501962306a36Sopenharmony_ci		size = cea_db_collection_size(ext);
502062306a36Sopenharmony_ci		if (!size)
502162306a36Sopenharmony_ci			continue;
502262306a36Sopenharmony_ci
502362306a36Sopenharmony_ci		iter->index = 4;
502462306a36Sopenharmony_ci		iter->end = iter->index + size;
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_ci		return ext;
502762306a36Sopenharmony_ci	}
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_ci	return NULL;
503062306a36Sopenharmony_ci}
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci/*
503362306a36Sopenharmony_ci * References:
503462306a36Sopenharmony_ci * - DisplayID v1.3 Appendix C: CEA Data Block within a DisplayID Data Block
503562306a36Sopenharmony_ci * - DisplayID v2.0 section 4.10 CTA DisplayID Data Block
503662306a36Sopenharmony_ci *
503762306a36Sopenharmony_ci * Note that the above do not specify any connection between DisplayID Data
503862306a36Sopenharmony_ci * Block revision and CTA Extension versions.
503962306a36Sopenharmony_ci */
504062306a36Sopenharmony_cistatic const void *__cea_db_iter_displayid_next(struct cea_db_iter *iter)
504162306a36Sopenharmony_ci{
504262306a36Sopenharmony_ci	const struct displayid_block *block;
504362306a36Sopenharmony_ci
504462306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter->displayid_iter) {
504562306a36Sopenharmony_ci		if (block->tag != DATA_BLOCK_CTA)
504662306a36Sopenharmony_ci			continue;
504762306a36Sopenharmony_ci
504862306a36Sopenharmony_ci		/*
504962306a36Sopenharmony_ci		 * The displayid iterator has already verified the block bounds
505062306a36Sopenharmony_ci		 * in displayid_iter_block().
505162306a36Sopenharmony_ci		 */
505262306a36Sopenharmony_ci		iter->index = sizeof(*block);
505362306a36Sopenharmony_ci		iter->end = iter->index + block->num_bytes;
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci		return block;
505662306a36Sopenharmony_ci	}
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci	return NULL;
505962306a36Sopenharmony_ci}
506062306a36Sopenharmony_ci
506162306a36Sopenharmony_cistatic const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter)
506262306a36Sopenharmony_ci{
506362306a36Sopenharmony_ci	const struct cea_db *db;
506462306a36Sopenharmony_ci
506562306a36Sopenharmony_ci	if (iter->collection) {
506662306a36Sopenharmony_ci		/* Current collection should always be valid. */
506762306a36Sopenharmony_ci		db = __cea_db_iter_current_block(iter);
506862306a36Sopenharmony_ci		if (WARN_ON(!db)) {
506962306a36Sopenharmony_ci			iter->collection = NULL;
507062306a36Sopenharmony_ci			return NULL;
507162306a36Sopenharmony_ci		}
507262306a36Sopenharmony_ci
507362306a36Sopenharmony_ci		/* Next block in CTA Data Block Collection */
507462306a36Sopenharmony_ci		iter->index += sizeof(*db) + cea_db_payload_len(db);
507562306a36Sopenharmony_ci
507662306a36Sopenharmony_ci		db = __cea_db_iter_current_block(iter);
507762306a36Sopenharmony_ci		if (db)
507862306a36Sopenharmony_ci			return db;
507962306a36Sopenharmony_ci	}
508062306a36Sopenharmony_ci
508162306a36Sopenharmony_ci	for (;;) {
508262306a36Sopenharmony_ci		/*
508362306a36Sopenharmony_ci		 * Find the next CTA Data Block Collection. First iterate all
508462306a36Sopenharmony_ci		 * the EDID CTA Extensions, then all the DisplayID CTA blocks.
508562306a36Sopenharmony_ci		 *
508662306a36Sopenharmony_ci		 * Per DisplayID v1.3 Appendix B: DisplayID as an EDID
508762306a36Sopenharmony_ci		 * Extension, it's recommended that DisplayID extensions are
508862306a36Sopenharmony_ci		 * exposed after all of the CTA Extensions.
508962306a36Sopenharmony_ci		 */
509062306a36Sopenharmony_ci		iter->collection = __cea_db_iter_edid_next(iter);
509162306a36Sopenharmony_ci		if (!iter->collection)
509262306a36Sopenharmony_ci			iter->collection = __cea_db_iter_displayid_next(iter);
509362306a36Sopenharmony_ci
509462306a36Sopenharmony_ci		if (!iter->collection)
509562306a36Sopenharmony_ci			return NULL;
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_ci		db = __cea_db_iter_current_block(iter);
509862306a36Sopenharmony_ci		if (db)
509962306a36Sopenharmony_ci			return db;
510062306a36Sopenharmony_ci	}
510162306a36Sopenharmony_ci}
510262306a36Sopenharmony_ci
510362306a36Sopenharmony_ci#define cea_db_iter_for_each(__db, __iter) \
510462306a36Sopenharmony_ci	while (((__db) = __cea_db_iter_next(__iter)))
510562306a36Sopenharmony_ci
510662306a36Sopenharmony_cistatic void cea_db_iter_end(struct cea_db_iter *iter)
510762306a36Sopenharmony_ci{
510862306a36Sopenharmony_ci	displayid_iter_end(&iter->displayid_iter);
510962306a36Sopenharmony_ci	drm_edid_iter_end(&iter->edid_iter);
511062306a36Sopenharmony_ci
511162306a36Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
511262306a36Sopenharmony_ci}
511362306a36Sopenharmony_ci
511462306a36Sopenharmony_cistatic bool cea_db_is_hdmi_vsdb(const struct cea_db *db)
511562306a36Sopenharmony_ci{
511662306a36Sopenharmony_ci	return cea_db_is_vendor(db, HDMI_IEEE_OUI) &&
511762306a36Sopenharmony_ci		cea_db_payload_len(db) >= 5;
511862306a36Sopenharmony_ci}
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_cistatic bool cea_db_is_hdmi_forum_vsdb(const struct cea_db *db)
512162306a36Sopenharmony_ci{
512262306a36Sopenharmony_ci	return cea_db_is_vendor(db, HDMI_FORUM_IEEE_OUI) &&
512362306a36Sopenharmony_ci		cea_db_payload_len(db) >= 7;
512462306a36Sopenharmony_ci}
512562306a36Sopenharmony_ci
512662306a36Sopenharmony_cistatic bool cea_db_is_hdmi_forum_eeodb(const void *db)
512762306a36Sopenharmony_ci{
512862306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_EEODB) &&
512962306a36Sopenharmony_ci		cea_db_payload_len(db) >= 2;
513062306a36Sopenharmony_ci}
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_cistatic bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
513362306a36Sopenharmony_ci{
513462306a36Sopenharmony_ci	return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) &&
513562306a36Sopenharmony_ci		cea_db_payload_len(db) == 21;
513662306a36Sopenharmony_ci}
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_cistatic bool cea_db_is_vcdb(const struct cea_db *db)
513962306a36Sopenharmony_ci{
514062306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
514162306a36Sopenharmony_ci		cea_db_payload_len(db) == 2;
514262306a36Sopenharmony_ci}
514362306a36Sopenharmony_ci
514462306a36Sopenharmony_cistatic bool cea_db_is_hdmi_forum_scdb(const struct cea_db *db)
514562306a36Sopenharmony_ci{
514662306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_SCDB) &&
514762306a36Sopenharmony_ci		cea_db_payload_len(db) >= 7;
514862306a36Sopenharmony_ci}
514962306a36Sopenharmony_ci
515062306a36Sopenharmony_cistatic bool cea_db_is_y420cmdb(const struct cea_db *db)
515162306a36Sopenharmony_ci{
515262306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_CAP_MAP);
515362306a36Sopenharmony_ci}
515462306a36Sopenharmony_ci
515562306a36Sopenharmony_cistatic bool cea_db_is_y420vdb(const struct cea_db *db)
515662306a36Sopenharmony_ci{
515762306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_DATA);
515862306a36Sopenharmony_ci}
515962306a36Sopenharmony_ci
516062306a36Sopenharmony_cistatic bool cea_db_is_hdmi_hdr_metadata_block(const struct cea_db *db)
516162306a36Sopenharmony_ci{
516262306a36Sopenharmony_ci	return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) &&
516362306a36Sopenharmony_ci		cea_db_payload_len(db) >= 3;
516462306a36Sopenharmony_ci}
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_ci/*
516762306a36Sopenharmony_ci * Get the HF-EEODB override extension block count from EDID.
516862306a36Sopenharmony_ci *
516962306a36Sopenharmony_ci * The passed in EDID may be partially read, as long as it has at least two
517062306a36Sopenharmony_ci * blocks (base block and one extension block) if EDID extension count is > 0.
517162306a36Sopenharmony_ci *
517262306a36Sopenharmony_ci * Note that this is *not* how you should parse CTA Data Blocks in general; this
517362306a36Sopenharmony_ci * is only to handle partially read EDIDs. Normally, use the CTA Data Block
517462306a36Sopenharmony_ci * iterators instead.
517562306a36Sopenharmony_ci *
517662306a36Sopenharmony_ci * References:
517762306a36Sopenharmony_ci * - HDMI 2.1 section 10.3.6 HDMI Forum EDID Extension Override Data Block
517862306a36Sopenharmony_ci */
517962306a36Sopenharmony_cistatic int edid_hfeeodb_extension_block_count(const struct edid *edid)
518062306a36Sopenharmony_ci{
518162306a36Sopenharmony_ci	const u8 *cta;
518262306a36Sopenharmony_ci
518362306a36Sopenharmony_ci	/* No extensions according to base block, no HF-EEODB. */
518462306a36Sopenharmony_ci	if (!edid_extension_block_count(edid))
518562306a36Sopenharmony_ci		return 0;
518662306a36Sopenharmony_ci
518762306a36Sopenharmony_ci	/* HF-EEODB is always in the first EDID extension block only */
518862306a36Sopenharmony_ci	cta = edid_extension_block_data(edid, 0);
518962306a36Sopenharmony_ci	if (edid_block_tag(cta) != CEA_EXT || cea_revision(cta) < 3)
519062306a36Sopenharmony_ci		return 0;
519162306a36Sopenharmony_ci
519262306a36Sopenharmony_ci	/* Need to have the data block collection, and at least 3 bytes. */
519362306a36Sopenharmony_ci	if (cea_db_collection_size(cta) < 3)
519462306a36Sopenharmony_ci		return 0;
519562306a36Sopenharmony_ci
519662306a36Sopenharmony_ci	/*
519762306a36Sopenharmony_ci	 * Sinks that include the HF-EEODB in their E-EDID shall include one and
519862306a36Sopenharmony_ci	 * only one instance of the HF-EEODB in the E-EDID, occupying bytes 4
519962306a36Sopenharmony_ci	 * through 6 of Block 1 of the E-EDID.
520062306a36Sopenharmony_ci	 */
520162306a36Sopenharmony_ci	if (!cea_db_is_hdmi_forum_eeodb(&cta[4]))
520262306a36Sopenharmony_ci		return 0;
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci	return cta[4 + 2];
520562306a36Sopenharmony_ci}
520662306a36Sopenharmony_ci
520762306a36Sopenharmony_ci/*
520862306a36Sopenharmony_ci * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB)
520962306a36Sopenharmony_ci *
521062306a36Sopenharmony_ci * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB,
521162306a36Sopenharmony_ci * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444
521262306a36Sopenharmony_ci * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can
521362306a36Sopenharmony_ci * support YCBCR420 output too.
521462306a36Sopenharmony_ci */
521562306a36Sopenharmony_cistatic void parse_cta_y420cmdb(struct drm_connector *connector,
521662306a36Sopenharmony_ci			       const struct cea_db *db, u64 *y420cmdb_map)
521762306a36Sopenharmony_ci{
521862306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
521962306a36Sopenharmony_ci	int i, map_len = cea_db_payload_len(db) - 1;
522062306a36Sopenharmony_ci	const u8 *data = cea_db_data(db) + 1;
522162306a36Sopenharmony_ci	u64 map = 0;
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	if (map_len == 0) {
522462306a36Sopenharmony_ci		/* All CEA modes support ycbcr420 sampling also.*/
522562306a36Sopenharmony_ci		map = U64_MAX;
522662306a36Sopenharmony_ci		goto out;
522762306a36Sopenharmony_ci	}
522862306a36Sopenharmony_ci
522962306a36Sopenharmony_ci	/*
523062306a36Sopenharmony_ci	 * This map indicates which of the existing CEA block modes
523162306a36Sopenharmony_ci	 * from VDB can support YCBCR420 output too. So if bit=0 is
523262306a36Sopenharmony_ci	 * set, first mode from VDB can support YCBCR420 output too.
523362306a36Sopenharmony_ci	 * We will parse and keep this map, before parsing VDB itself
523462306a36Sopenharmony_ci	 * to avoid going through the same block again and again.
523562306a36Sopenharmony_ci	 *
523662306a36Sopenharmony_ci	 * Spec is not clear about max possible size of this block.
523762306a36Sopenharmony_ci	 * Clamping max bitmap block size at 8 bytes. Every byte can
523862306a36Sopenharmony_ci	 * address 8 CEA modes, in this way this map can address
523962306a36Sopenharmony_ci	 * 8*8 = first 64 SVDs.
524062306a36Sopenharmony_ci	 */
524162306a36Sopenharmony_ci	if (WARN_ON_ONCE(map_len > 8))
524262306a36Sopenharmony_ci		map_len = 8;
524362306a36Sopenharmony_ci
524462306a36Sopenharmony_ci	for (i = 0; i < map_len; i++)
524562306a36Sopenharmony_ci		map |= (u64)data[i] << (8 * i);
524662306a36Sopenharmony_ci
524762306a36Sopenharmony_ciout:
524862306a36Sopenharmony_ci	if (map)
524962306a36Sopenharmony_ci		info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
525062306a36Sopenharmony_ci
525162306a36Sopenharmony_ci	*y420cmdb_map = map;
525262306a36Sopenharmony_ci}
525362306a36Sopenharmony_ci
525462306a36Sopenharmony_cistatic int add_cea_modes(struct drm_connector *connector,
525562306a36Sopenharmony_ci			 const struct drm_edid *drm_edid)
525662306a36Sopenharmony_ci{
525762306a36Sopenharmony_ci	const struct cea_db *db;
525862306a36Sopenharmony_ci	struct cea_db_iter iter;
525962306a36Sopenharmony_ci	int modes;
526062306a36Sopenharmony_ci
526162306a36Sopenharmony_ci	/* CTA VDB block VICs parsed earlier */
526262306a36Sopenharmony_ci	modes = add_cta_vdb_modes(connector);
526362306a36Sopenharmony_ci
526462306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
526562306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
526662306a36Sopenharmony_ci		if (cea_db_is_hdmi_vsdb(db)) {
526762306a36Sopenharmony_ci			modes += do_hdmi_vsdb_modes(connector, (const u8 *)db,
526862306a36Sopenharmony_ci						    cea_db_payload_len(db));
526962306a36Sopenharmony_ci		} else if (cea_db_is_y420vdb(db)) {
527062306a36Sopenharmony_ci			const u8 *vdb420 = cea_db_data(db) + 1;
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_ci			/* Add 4:2:0(only) modes present in EDID */
527362306a36Sopenharmony_ci			modes += do_y420vdb_modes(connector, vdb420,
527462306a36Sopenharmony_ci						  cea_db_payload_len(db) - 1);
527562306a36Sopenharmony_ci		}
527662306a36Sopenharmony_ci	}
527762306a36Sopenharmony_ci	cea_db_iter_end(&iter);
527862306a36Sopenharmony_ci
527962306a36Sopenharmony_ci	return modes;
528062306a36Sopenharmony_ci}
528162306a36Sopenharmony_ci
528262306a36Sopenharmony_cistatic void fixup_detailed_cea_mode_clock(struct drm_connector *connector,
528362306a36Sopenharmony_ci					  struct drm_display_mode *mode)
528462306a36Sopenharmony_ci{
528562306a36Sopenharmony_ci	const struct drm_display_mode *cea_mode;
528662306a36Sopenharmony_ci	int clock1, clock2, clock;
528762306a36Sopenharmony_ci	u8 vic;
528862306a36Sopenharmony_ci	const char *type;
528962306a36Sopenharmony_ci
529062306a36Sopenharmony_ci	/*
529162306a36Sopenharmony_ci	 * allow 5kHz clock difference either way to account for
529262306a36Sopenharmony_ci	 * the 10kHz clock resolution limit of detailed timings.
529362306a36Sopenharmony_ci	 */
529462306a36Sopenharmony_ci	vic = drm_match_cea_mode_clock_tolerance(mode, 5);
529562306a36Sopenharmony_ci	if (drm_valid_cea_vic(vic)) {
529662306a36Sopenharmony_ci		type = "CEA";
529762306a36Sopenharmony_ci		cea_mode = cea_mode_for_vic(vic);
529862306a36Sopenharmony_ci		clock1 = cea_mode->clock;
529962306a36Sopenharmony_ci		clock2 = cea_mode_alternate_clock(cea_mode);
530062306a36Sopenharmony_ci	} else {
530162306a36Sopenharmony_ci		vic = drm_match_hdmi_mode_clock_tolerance(mode, 5);
530262306a36Sopenharmony_ci		if (drm_valid_hdmi_vic(vic)) {
530362306a36Sopenharmony_ci			type = "HDMI";
530462306a36Sopenharmony_ci			cea_mode = &edid_4k_modes[vic];
530562306a36Sopenharmony_ci			clock1 = cea_mode->clock;
530662306a36Sopenharmony_ci			clock2 = hdmi_mode_alternate_clock(cea_mode);
530762306a36Sopenharmony_ci		} else {
530862306a36Sopenharmony_ci			return;
530962306a36Sopenharmony_ci		}
531062306a36Sopenharmony_ci	}
531162306a36Sopenharmony_ci
531262306a36Sopenharmony_ci	/* pick whichever is closest */
531362306a36Sopenharmony_ci	if (abs(mode->clock - clock1) < abs(mode->clock - clock2))
531462306a36Sopenharmony_ci		clock = clock1;
531562306a36Sopenharmony_ci	else
531662306a36Sopenharmony_ci		clock = clock2;
531762306a36Sopenharmony_ci
531862306a36Sopenharmony_ci	if (mode->clock == clock)
531962306a36Sopenharmony_ci		return;
532062306a36Sopenharmony_ci
532162306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
532262306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
532362306a36Sopenharmony_ci		    connector->base.id, connector->name,
532462306a36Sopenharmony_ci		    type, vic, mode->clock, clock);
532562306a36Sopenharmony_ci	mode->clock = clock;
532662306a36Sopenharmony_ci}
532762306a36Sopenharmony_ci
532862306a36Sopenharmony_cistatic void drm_calculate_luminance_range(struct drm_connector *connector)
532962306a36Sopenharmony_ci{
533062306a36Sopenharmony_ci	struct hdr_static_metadata *hdr_metadata = &connector->hdr_sink_metadata.hdmi_type1;
533162306a36Sopenharmony_ci	struct drm_luminance_range_info *luminance_range =
533262306a36Sopenharmony_ci		&connector->display_info.luminance_range;
533362306a36Sopenharmony_ci	static const u8 pre_computed_values[] = {
533462306a36Sopenharmony_ci		50, 51, 52, 53, 55, 56, 57, 58, 59, 61, 62, 63, 65, 66, 68, 69,
533562306a36Sopenharmony_ci		71, 72, 74, 75, 77, 79, 81, 82, 84, 86, 88, 90, 92, 94, 96, 98
533662306a36Sopenharmony_ci	};
533762306a36Sopenharmony_ci	u32 max_avg, min_cll, max, min, q, r;
533862306a36Sopenharmony_ci
533962306a36Sopenharmony_ci	if (!(hdr_metadata->metadata_type & BIT(HDMI_STATIC_METADATA_TYPE1)))
534062306a36Sopenharmony_ci		return;
534162306a36Sopenharmony_ci
534262306a36Sopenharmony_ci	max_avg = hdr_metadata->max_fall;
534362306a36Sopenharmony_ci	min_cll = hdr_metadata->min_cll;
534462306a36Sopenharmony_ci
534562306a36Sopenharmony_ci	/*
534662306a36Sopenharmony_ci	 * From the specification (CTA-861-G), for calculating the maximum
534762306a36Sopenharmony_ci	 * luminance we need to use:
534862306a36Sopenharmony_ci	 *	Luminance = 50*2**(CV/32)
534962306a36Sopenharmony_ci	 * Where CV is a one-byte value.
535062306a36Sopenharmony_ci	 * For calculating this expression we may need float point precision;
535162306a36Sopenharmony_ci	 * to avoid this complexity level, we take advantage that CV is divided
535262306a36Sopenharmony_ci	 * by a constant. From the Euclids division algorithm, we know that CV
535362306a36Sopenharmony_ci	 * can be written as: CV = 32*q + r. Next, we replace CV in the
535462306a36Sopenharmony_ci	 * Luminance expression and get 50*(2**q)*(2**(r/32)), hence we just
535562306a36Sopenharmony_ci	 * need to pre-compute the value of r/32. For pre-computing the values
535662306a36Sopenharmony_ci	 * We just used the following Ruby line:
535762306a36Sopenharmony_ci	 *	(0...32).each {|cv| puts (50*2**(cv/32.0)).round}
535862306a36Sopenharmony_ci	 * The results of the above expressions can be verified at
535962306a36Sopenharmony_ci	 * pre_computed_values.
536062306a36Sopenharmony_ci	 */
536162306a36Sopenharmony_ci	q = max_avg >> 5;
536262306a36Sopenharmony_ci	r = max_avg % 32;
536362306a36Sopenharmony_ci	max = (1 << q) * pre_computed_values[r];
536462306a36Sopenharmony_ci
536562306a36Sopenharmony_ci	/* min luminance: maxLum * (CV/255)^2 / 100 */
536662306a36Sopenharmony_ci	q = DIV_ROUND_CLOSEST(min_cll, 255);
536762306a36Sopenharmony_ci	min = max * DIV_ROUND_CLOSEST((q * q), 100);
536862306a36Sopenharmony_ci
536962306a36Sopenharmony_ci	luminance_range->min_luminance = min;
537062306a36Sopenharmony_ci	luminance_range->max_luminance = max;
537162306a36Sopenharmony_ci}
537262306a36Sopenharmony_ci
537362306a36Sopenharmony_cistatic uint8_t eotf_supported(const u8 *edid_ext)
537462306a36Sopenharmony_ci{
537562306a36Sopenharmony_ci	return edid_ext[2] &
537662306a36Sopenharmony_ci		(BIT(HDMI_EOTF_TRADITIONAL_GAMMA_SDR) |
537762306a36Sopenharmony_ci		 BIT(HDMI_EOTF_TRADITIONAL_GAMMA_HDR) |
537862306a36Sopenharmony_ci		 BIT(HDMI_EOTF_SMPTE_ST2084) |
537962306a36Sopenharmony_ci		 BIT(HDMI_EOTF_BT_2100_HLG));
538062306a36Sopenharmony_ci}
538162306a36Sopenharmony_ci
538262306a36Sopenharmony_cistatic uint8_t hdr_metadata_type(const u8 *edid_ext)
538362306a36Sopenharmony_ci{
538462306a36Sopenharmony_ci	return edid_ext[3] &
538562306a36Sopenharmony_ci		BIT(HDMI_STATIC_METADATA_TYPE1);
538662306a36Sopenharmony_ci}
538762306a36Sopenharmony_ci
538862306a36Sopenharmony_cistatic void
538962306a36Sopenharmony_cidrm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db)
539062306a36Sopenharmony_ci{
539162306a36Sopenharmony_ci	u16 len;
539262306a36Sopenharmony_ci
539362306a36Sopenharmony_ci	len = cea_db_payload_len(db);
539462306a36Sopenharmony_ci
539562306a36Sopenharmony_ci	connector->hdr_sink_metadata.hdmi_type1.eotf =
539662306a36Sopenharmony_ci						eotf_supported(db);
539762306a36Sopenharmony_ci	connector->hdr_sink_metadata.hdmi_type1.metadata_type =
539862306a36Sopenharmony_ci						hdr_metadata_type(db);
539962306a36Sopenharmony_ci
540062306a36Sopenharmony_ci	if (len >= 4)
540162306a36Sopenharmony_ci		connector->hdr_sink_metadata.hdmi_type1.max_cll = db[4];
540262306a36Sopenharmony_ci	if (len >= 5)
540362306a36Sopenharmony_ci		connector->hdr_sink_metadata.hdmi_type1.max_fall = db[5];
540462306a36Sopenharmony_ci	if (len >= 6) {
540562306a36Sopenharmony_ci		connector->hdr_sink_metadata.hdmi_type1.min_cll = db[6];
540662306a36Sopenharmony_ci
540762306a36Sopenharmony_ci		/* Calculate only when all values are available */
540862306a36Sopenharmony_ci		drm_calculate_luminance_range(connector);
540962306a36Sopenharmony_ci	}
541062306a36Sopenharmony_ci}
541162306a36Sopenharmony_ci
541262306a36Sopenharmony_ci/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
541362306a36Sopenharmony_cistatic void
541462306a36Sopenharmony_cidrm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
541562306a36Sopenharmony_ci{
541662306a36Sopenharmony_ci	u8 len = cea_db_payload_len(db);
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	if (len >= 6 && (db[6] & (1 << 7)))
541962306a36Sopenharmony_ci		connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI;
542062306a36Sopenharmony_ci
542162306a36Sopenharmony_ci	if (len >= 10 && hdmi_vsdb_latency_present(db)) {
542262306a36Sopenharmony_ci		connector->latency_present[0] = true;
542362306a36Sopenharmony_ci		connector->video_latency[0] = db[9];
542462306a36Sopenharmony_ci		connector->audio_latency[0] = db[10];
542562306a36Sopenharmony_ci	}
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci	if (len >= 12 && hdmi_vsdb_i_latency_present(db)) {
542862306a36Sopenharmony_ci		connector->latency_present[1] = true;
542962306a36Sopenharmony_ci		connector->video_latency[1] = db[11];
543062306a36Sopenharmony_ci		connector->audio_latency[1] = db[12];
543162306a36Sopenharmony_ci	}
543262306a36Sopenharmony_ci
543362306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
543462306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n",
543562306a36Sopenharmony_ci		    connector->base.id, connector->name,
543662306a36Sopenharmony_ci		    connector->latency_present[0], connector->latency_present[1],
543762306a36Sopenharmony_ci		    connector->video_latency[0], connector->video_latency[1],
543862306a36Sopenharmony_ci		    connector->audio_latency[0], connector->audio_latency[1]);
543962306a36Sopenharmony_ci}
544062306a36Sopenharmony_ci
544162306a36Sopenharmony_cistatic void
544262306a36Sopenharmony_cimonitor_name(const struct detailed_timing *timing, void *data)
544362306a36Sopenharmony_ci{
544462306a36Sopenharmony_ci	const char **res = data;
544562306a36Sopenharmony_ci
544662306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_NAME))
544762306a36Sopenharmony_ci		return;
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_ci	*res = timing->data.other_data.data.str.str;
545062306a36Sopenharmony_ci}
545162306a36Sopenharmony_ci
545262306a36Sopenharmony_cistatic int get_monitor_name(const struct drm_edid *drm_edid, char name[13])
545362306a36Sopenharmony_ci{
545462306a36Sopenharmony_ci	const char *edid_name = NULL;
545562306a36Sopenharmony_ci	int mnl;
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_ci	if (!drm_edid || !name)
545862306a36Sopenharmony_ci		return 0;
545962306a36Sopenharmony_ci
546062306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, monitor_name, &edid_name);
546162306a36Sopenharmony_ci	for (mnl = 0; edid_name && mnl < 13; mnl++) {
546262306a36Sopenharmony_ci		if (edid_name[mnl] == 0x0a)
546362306a36Sopenharmony_ci			break;
546462306a36Sopenharmony_ci
546562306a36Sopenharmony_ci		name[mnl] = edid_name[mnl];
546662306a36Sopenharmony_ci	}
546762306a36Sopenharmony_ci
546862306a36Sopenharmony_ci	return mnl;
546962306a36Sopenharmony_ci}
547062306a36Sopenharmony_ci
547162306a36Sopenharmony_ci/**
547262306a36Sopenharmony_ci * drm_edid_get_monitor_name - fetch the monitor name from the edid
547362306a36Sopenharmony_ci * @edid: monitor EDID information
547462306a36Sopenharmony_ci * @name: pointer to a character array to hold the name of the monitor
547562306a36Sopenharmony_ci * @bufsize: The size of the name buffer (should be at least 14 chars.)
547662306a36Sopenharmony_ci *
547762306a36Sopenharmony_ci */
547862306a36Sopenharmony_civoid drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize)
547962306a36Sopenharmony_ci{
548062306a36Sopenharmony_ci	int name_length = 0;
548162306a36Sopenharmony_ci
548262306a36Sopenharmony_ci	if (bufsize <= 0)
548362306a36Sopenharmony_ci		return;
548462306a36Sopenharmony_ci
548562306a36Sopenharmony_ci	if (edid) {
548662306a36Sopenharmony_ci		char buf[13];
548762306a36Sopenharmony_ci		struct drm_edid drm_edid = {
548862306a36Sopenharmony_ci			.edid = edid,
548962306a36Sopenharmony_ci			.size = edid_size(edid),
549062306a36Sopenharmony_ci		};
549162306a36Sopenharmony_ci
549262306a36Sopenharmony_ci		name_length = min(get_monitor_name(&drm_edid, buf), bufsize - 1);
549362306a36Sopenharmony_ci		memcpy(name, buf, name_length);
549462306a36Sopenharmony_ci	}
549562306a36Sopenharmony_ci
549662306a36Sopenharmony_ci	name[name_length] = '\0';
549762306a36Sopenharmony_ci}
549862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_get_monitor_name);
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_cistatic void clear_eld(struct drm_connector *connector)
550162306a36Sopenharmony_ci{
550262306a36Sopenharmony_ci	memset(connector->eld, 0, sizeof(connector->eld));
550362306a36Sopenharmony_ci
550462306a36Sopenharmony_ci	connector->latency_present[0] = false;
550562306a36Sopenharmony_ci	connector->latency_present[1] = false;
550662306a36Sopenharmony_ci	connector->video_latency[0] = 0;
550762306a36Sopenharmony_ci	connector->audio_latency[0] = 0;
550862306a36Sopenharmony_ci	connector->video_latency[1] = 0;
550962306a36Sopenharmony_ci	connector->audio_latency[1] = 0;
551062306a36Sopenharmony_ci}
551162306a36Sopenharmony_ci
551262306a36Sopenharmony_ci/*
551362306a36Sopenharmony_ci * drm_edid_to_eld - build ELD from EDID
551462306a36Sopenharmony_ci * @connector: connector corresponding to the HDMI/DP sink
551562306a36Sopenharmony_ci * @drm_edid: EDID to parse
551662306a36Sopenharmony_ci *
551762306a36Sopenharmony_ci * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
551862306a36Sopenharmony_ci * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
551962306a36Sopenharmony_ci */
552062306a36Sopenharmony_cistatic void drm_edid_to_eld(struct drm_connector *connector,
552162306a36Sopenharmony_ci			    const struct drm_edid *drm_edid)
552262306a36Sopenharmony_ci{
552362306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
552462306a36Sopenharmony_ci	const struct cea_db *db;
552562306a36Sopenharmony_ci	struct cea_db_iter iter;
552662306a36Sopenharmony_ci	uint8_t *eld = connector->eld;
552762306a36Sopenharmony_ci	int total_sad_count = 0;
552862306a36Sopenharmony_ci	int mnl;
552962306a36Sopenharmony_ci
553062306a36Sopenharmony_ci	if (!drm_edid)
553162306a36Sopenharmony_ci		return;
553262306a36Sopenharmony_ci
553362306a36Sopenharmony_ci	mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
553462306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD monitor %s\n",
553562306a36Sopenharmony_ci		    connector->base.id, connector->name,
553662306a36Sopenharmony_ci		    &eld[DRM_ELD_MONITOR_NAME_STRING]);
553762306a36Sopenharmony_ci
553862306a36Sopenharmony_ci	eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT;
553962306a36Sopenharmony_ci	eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl;
554062306a36Sopenharmony_ci
554162306a36Sopenharmony_ci	eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D;
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci	eld[DRM_ELD_MANUFACTURER_NAME0] = drm_edid->edid->mfg_id[0];
554462306a36Sopenharmony_ci	eld[DRM_ELD_MANUFACTURER_NAME1] = drm_edid->edid->mfg_id[1];
554562306a36Sopenharmony_ci	eld[DRM_ELD_PRODUCT_CODE0] = drm_edid->edid->prod_code[0];
554662306a36Sopenharmony_ci	eld[DRM_ELD_PRODUCT_CODE1] = drm_edid->edid->prod_code[1];
554762306a36Sopenharmony_ci
554862306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
554962306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
555062306a36Sopenharmony_ci		const u8 *data = cea_db_data(db);
555162306a36Sopenharmony_ci		int len = cea_db_payload_len(db);
555262306a36Sopenharmony_ci		int sad_count;
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_ci		switch (cea_db_tag(db)) {
555562306a36Sopenharmony_ci		case CTA_DB_AUDIO:
555662306a36Sopenharmony_ci			/* Audio Data Block, contains SADs */
555762306a36Sopenharmony_ci			sad_count = min(len / 3, 15 - total_sad_count);
555862306a36Sopenharmony_ci			if (sad_count >= 1)
555962306a36Sopenharmony_ci				memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
556062306a36Sopenharmony_ci				       data, sad_count * 3);
556162306a36Sopenharmony_ci			total_sad_count += sad_count;
556262306a36Sopenharmony_ci			break;
556362306a36Sopenharmony_ci		case CTA_DB_SPEAKER:
556462306a36Sopenharmony_ci			/* Speaker Allocation Data Block */
556562306a36Sopenharmony_ci			if (len >= 1)
556662306a36Sopenharmony_ci				eld[DRM_ELD_SPEAKER] = data[0];
556762306a36Sopenharmony_ci			break;
556862306a36Sopenharmony_ci		case CTA_DB_VENDOR:
556962306a36Sopenharmony_ci			/* HDMI Vendor-Specific Data Block */
557062306a36Sopenharmony_ci			if (cea_db_is_hdmi_vsdb(db))
557162306a36Sopenharmony_ci				drm_parse_hdmi_vsdb_audio(connector, (const u8 *)db);
557262306a36Sopenharmony_ci			break;
557362306a36Sopenharmony_ci		default:
557462306a36Sopenharmony_ci			break;
557562306a36Sopenharmony_ci		}
557662306a36Sopenharmony_ci	}
557762306a36Sopenharmony_ci	cea_db_iter_end(&iter);
557862306a36Sopenharmony_ci
557962306a36Sopenharmony_ci	eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT;
558062306a36Sopenharmony_ci
558162306a36Sopenharmony_ci	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
558262306a36Sopenharmony_ci	    connector->connector_type == DRM_MODE_CONNECTOR_eDP)
558362306a36Sopenharmony_ci		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
558462306a36Sopenharmony_ci	else
558562306a36Sopenharmony_ci		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
558662306a36Sopenharmony_ci
558762306a36Sopenharmony_ci	eld[DRM_ELD_BASELINE_ELD_LEN] =
558862306a36Sopenharmony_ci		DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD size %d, SAD count %d\n",
559162306a36Sopenharmony_ci		    connector->base.id, connector->name,
559262306a36Sopenharmony_ci		    drm_eld_size(eld), total_sad_count);
559362306a36Sopenharmony_ci}
559462306a36Sopenharmony_ci
559562306a36Sopenharmony_cistatic int _drm_edid_to_sad(const struct drm_edid *drm_edid,
559662306a36Sopenharmony_ci			    struct cea_sad **sads)
559762306a36Sopenharmony_ci{
559862306a36Sopenharmony_ci	const struct cea_db *db;
559962306a36Sopenharmony_ci	struct cea_db_iter iter;
560062306a36Sopenharmony_ci	int count = 0;
560162306a36Sopenharmony_ci
560262306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
560362306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
560462306a36Sopenharmony_ci		if (cea_db_tag(db) == CTA_DB_AUDIO) {
560562306a36Sopenharmony_ci			int j;
560662306a36Sopenharmony_ci
560762306a36Sopenharmony_ci			count = cea_db_payload_len(db) / 3; /* SAD is 3B */
560862306a36Sopenharmony_ci			*sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
560962306a36Sopenharmony_ci			if (!*sads)
561062306a36Sopenharmony_ci				return -ENOMEM;
561162306a36Sopenharmony_ci			for (j = 0; j < count; j++) {
561262306a36Sopenharmony_ci				const u8 *sad = &db->data[j * 3];
561362306a36Sopenharmony_ci
561462306a36Sopenharmony_ci				(*sads)[j].format = (sad[0] & 0x78) >> 3;
561562306a36Sopenharmony_ci				(*sads)[j].channels = sad[0] & 0x7;
561662306a36Sopenharmony_ci				(*sads)[j].freq = sad[1] & 0x7F;
561762306a36Sopenharmony_ci				(*sads)[j].byte2 = sad[2];
561862306a36Sopenharmony_ci			}
561962306a36Sopenharmony_ci			break;
562062306a36Sopenharmony_ci		}
562162306a36Sopenharmony_ci	}
562262306a36Sopenharmony_ci	cea_db_iter_end(&iter);
562362306a36Sopenharmony_ci
562462306a36Sopenharmony_ci	DRM_DEBUG_KMS("Found %d Short Audio Descriptors\n", count);
562562306a36Sopenharmony_ci
562662306a36Sopenharmony_ci	return count;
562762306a36Sopenharmony_ci}
562862306a36Sopenharmony_ci
562962306a36Sopenharmony_ci/**
563062306a36Sopenharmony_ci * drm_edid_to_sad - extracts SADs from EDID
563162306a36Sopenharmony_ci * @edid: EDID to parse
563262306a36Sopenharmony_ci * @sads: pointer that will be set to the extracted SADs
563362306a36Sopenharmony_ci *
563462306a36Sopenharmony_ci * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
563562306a36Sopenharmony_ci *
563662306a36Sopenharmony_ci * Note: The returned pointer needs to be freed using kfree().
563762306a36Sopenharmony_ci *
563862306a36Sopenharmony_ci * Return: The number of found SADs or negative number on error.
563962306a36Sopenharmony_ci */
564062306a36Sopenharmony_ciint drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads)
564162306a36Sopenharmony_ci{
564262306a36Sopenharmony_ci	struct drm_edid drm_edid;
564362306a36Sopenharmony_ci
564462306a36Sopenharmony_ci	return _drm_edid_to_sad(drm_edid_legacy_init(&drm_edid, edid), sads);
564562306a36Sopenharmony_ci}
564662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_to_sad);
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_cistatic int _drm_edid_to_speaker_allocation(const struct drm_edid *drm_edid,
564962306a36Sopenharmony_ci					   u8 **sadb)
565062306a36Sopenharmony_ci{
565162306a36Sopenharmony_ci	const struct cea_db *db;
565262306a36Sopenharmony_ci	struct cea_db_iter iter;
565362306a36Sopenharmony_ci	int count = 0;
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
565662306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
565762306a36Sopenharmony_ci		if (cea_db_tag(db) == CTA_DB_SPEAKER &&
565862306a36Sopenharmony_ci		    cea_db_payload_len(db) == 3) {
565962306a36Sopenharmony_ci			*sadb = kmemdup(db->data, cea_db_payload_len(db),
566062306a36Sopenharmony_ci					GFP_KERNEL);
566162306a36Sopenharmony_ci			if (!*sadb)
566262306a36Sopenharmony_ci				return -ENOMEM;
566362306a36Sopenharmony_ci			count = cea_db_payload_len(db);
566462306a36Sopenharmony_ci			break;
566562306a36Sopenharmony_ci		}
566662306a36Sopenharmony_ci	}
566762306a36Sopenharmony_ci	cea_db_iter_end(&iter);
566862306a36Sopenharmony_ci
566962306a36Sopenharmony_ci	DRM_DEBUG_KMS("Found %d Speaker Allocation Data Blocks\n", count);
567062306a36Sopenharmony_ci
567162306a36Sopenharmony_ci	return count;
567262306a36Sopenharmony_ci}
567362306a36Sopenharmony_ci
567462306a36Sopenharmony_ci/**
567562306a36Sopenharmony_ci * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
567662306a36Sopenharmony_ci * @edid: EDID to parse
567762306a36Sopenharmony_ci * @sadb: pointer to the speaker block
567862306a36Sopenharmony_ci *
567962306a36Sopenharmony_ci * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
568062306a36Sopenharmony_ci *
568162306a36Sopenharmony_ci * Note: The returned pointer needs to be freed using kfree().
568262306a36Sopenharmony_ci *
568362306a36Sopenharmony_ci * Return: The number of found Speaker Allocation Blocks or negative number on
568462306a36Sopenharmony_ci * error.
568562306a36Sopenharmony_ci */
568662306a36Sopenharmony_ciint drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
568762306a36Sopenharmony_ci{
568862306a36Sopenharmony_ci	struct drm_edid drm_edid;
568962306a36Sopenharmony_ci
569062306a36Sopenharmony_ci	return _drm_edid_to_speaker_allocation(drm_edid_legacy_init(&drm_edid, edid),
569162306a36Sopenharmony_ci					       sadb);
569262306a36Sopenharmony_ci}
569362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_to_speaker_allocation);
569462306a36Sopenharmony_ci
569562306a36Sopenharmony_ci/**
569662306a36Sopenharmony_ci * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
569762306a36Sopenharmony_ci * @connector: connector associated with the HDMI/DP sink
569862306a36Sopenharmony_ci * @mode: the display mode
569962306a36Sopenharmony_ci *
570062306a36Sopenharmony_ci * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
570162306a36Sopenharmony_ci * the sink doesn't support audio or video.
570262306a36Sopenharmony_ci */
570362306a36Sopenharmony_ciint drm_av_sync_delay(struct drm_connector *connector,
570462306a36Sopenharmony_ci		      const struct drm_display_mode *mode)
570562306a36Sopenharmony_ci{
570662306a36Sopenharmony_ci	int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
570762306a36Sopenharmony_ci	int a, v;
570862306a36Sopenharmony_ci
570962306a36Sopenharmony_ci	if (!connector->latency_present[0])
571062306a36Sopenharmony_ci		return 0;
571162306a36Sopenharmony_ci	if (!connector->latency_present[1])
571262306a36Sopenharmony_ci		i = 0;
571362306a36Sopenharmony_ci
571462306a36Sopenharmony_ci	a = connector->audio_latency[i];
571562306a36Sopenharmony_ci	v = connector->video_latency[i];
571662306a36Sopenharmony_ci
571762306a36Sopenharmony_ci	/*
571862306a36Sopenharmony_ci	 * HDMI/DP sink doesn't support audio or video?
571962306a36Sopenharmony_ci	 */
572062306a36Sopenharmony_ci	if (a == 255 || v == 255)
572162306a36Sopenharmony_ci		return 0;
572262306a36Sopenharmony_ci
572362306a36Sopenharmony_ci	/*
572462306a36Sopenharmony_ci	 * Convert raw EDID values to millisecond.
572562306a36Sopenharmony_ci	 * Treat unknown latency as 0ms.
572662306a36Sopenharmony_ci	 */
572762306a36Sopenharmony_ci	if (a)
572862306a36Sopenharmony_ci		a = min(2 * (a - 1), 500);
572962306a36Sopenharmony_ci	if (v)
573062306a36Sopenharmony_ci		v = min(2 * (v - 1), 500);
573162306a36Sopenharmony_ci
573262306a36Sopenharmony_ci	return max(v - a, 0);
573362306a36Sopenharmony_ci}
573462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_av_sync_delay);
573562306a36Sopenharmony_ci
573662306a36Sopenharmony_cistatic bool _drm_detect_hdmi_monitor(const struct drm_edid *drm_edid)
573762306a36Sopenharmony_ci{
573862306a36Sopenharmony_ci	const struct cea_db *db;
573962306a36Sopenharmony_ci	struct cea_db_iter iter;
574062306a36Sopenharmony_ci	bool hdmi = false;
574162306a36Sopenharmony_ci
574262306a36Sopenharmony_ci	/*
574362306a36Sopenharmony_ci	 * Because HDMI identifier is in Vendor Specific Block,
574462306a36Sopenharmony_ci	 * search it from all data blocks of CEA extension.
574562306a36Sopenharmony_ci	 */
574662306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
574762306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
574862306a36Sopenharmony_ci		if (cea_db_is_hdmi_vsdb(db)) {
574962306a36Sopenharmony_ci			hdmi = true;
575062306a36Sopenharmony_ci			break;
575162306a36Sopenharmony_ci		}
575262306a36Sopenharmony_ci	}
575362306a36Sopenharmony_ci	cea_db_iter_end(&iter);
575462306a36Sopenharmony_ci
575562306a36Sopenharmony_ci	return hdmi;
575662306a36Sopenharmony_ci}
575762306a36Sopenharmony_ci
575862306a36Sopenharmony_ci/**
575962306a36Sopenharmony_ci * drm_detect_hdmi_monitor - detect whether monitor is HDMI
576062306a36Sopenharmony_ci * @edid: monitor EDID information
576162306a36Sopenharmony_ci *
576262306a36Sopenharmony_ci * Parse the CEA extension according to CEA-861-B.
576362306a36Sopenharmony_ci *
576462306a36Sopenharmony_ci * Drivers that have added the modes parsed from EDID to drm_display_info
576562306a36Sopenharmony_ci * should use &drm_display_info.is_hdmi instead of calling this function.
576662306a36Sopenharmony_ci *
576762306a36Sopenharmony_ci * Return: True if the monitor is HDMI, false if not or unknown.
576862306a36Sopenharmony_ci */
576962306a36Sopenharmony_cibool drm_detect_hdmi_monitor(const struct edid *edid)
577062306a36Sopenharmony_ci{
577162306a36Sopenharmony_ci	struct drm_edid drm_edid;
577262306a36Sopenharmony_ci
577362306a36Sopenharmony_ci	return _drm_detect_hdmi_monitor(drm_edid_legacy_init(&drm_edid, edid));
577462306a36Sopenharmony_ci}
577562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_detect_hdmi_monitor);
577662306a36Sopenharmony_ci
577762306a36Sopenharmony_cistatic bool _drm_detect_monitor_audio(const struct drm_edid *drm_edid)
577862306a36Sopenharmony_ci{
577962306a36Sopenharmony_ci	struct drm_edid_iter edid_iter;
578062306a36Sopenharmony_ci	const struct cea_db *db;
578162306a36Sopenharmony_ci	struct cea_db_iter iter;
578262306a36Sopenharmony_ci	const u8 *edid_ext;
578362306a36Sopenharmony_ci	bool has_audio = false;
578462306a36Sopenharmony_ci
578562306a36Sopenharmony_ci	drm_edid_iter_begin(drm_edid, &edid_iter);
578662306a36Sopenharmony_ci	drm_edid_iter_for_each(edid_ext, &edid_iter) {
578762306a36Sopenharmony_ci		if (edid_ext[0] == CEA_EXT) {
578862306a36Sopenharmony_ci			has_audio = edid_ext[3] & EDID_BASIC_AUDIO;
578962306a36Sopenharmony_ci			if (has_audio)
579062306a36Sopenharmony_ci				break;
579162306a36Sopenharmony_ci		}
579262306a36Sopenharmony_ci	}
579362306a36Sopenharmony_ci	drm_edid_iter_end(&edid_iter);
579462306a36Sopenharmony_ci
579562306a36Sopenharmony_ci	if (has_audio) {
579662306a36Sopenharmony_ci		DRM_DEBUG_KMS("Monitor has basic audio support\n");
579762306a36Sopenharmony_ci		goto end;
579862306a36Sopenharmony_ci	}
579962306a36Sopenharmony_ci
580062306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
580162306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
580262306a36Sopenharmony_ci		if (cea_db_tag(db) == CTA_DB_AUDIO) {
580362306a36Sopenharmony_ci			const u8 *data = cea_db_data(db);
580462306a36Sopenharmony_ci			int i;
580562306a36Sopenharmony_ci
580662306a36Sopenharmony_ci			for (i = 0; i < cea_db_payload_len(db); i += 3)
580762306a36Sopenharmony_ci				DRM_DEBUG_KMS("CEA audio format %d\n",
580862306a36Sopenharmony_ci					      (data[i] >> 3) & 0xf);
580962306a36Sopenharmony_ci			has_audio = true;
581062306a36Sopenharmony_ci			break;
581162306a36Sopenharmony_ci		}
581262306a36Sopenharmony_ci	}
581362306a36Sopenharmony_ci	cea_db_iter_end(&iter);
581462306a36Sopenharmony_ci
581562306a36Sopenharmony_ciend:
581662306a36Sopenharmony_ci	return has_audio;
581762306a36Sopenharmony_ci}
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_ci/**
582062306a36Sopenharmony_ci * drm_detect_monitor_audio - check monitor audio capability
582162306a36Sopenharmony_ci * @edid: EDID block to scan
582262306a36Sopenharmony_ci *
582362306a36Sopenharmony_ci * Monitor should have CEA extension block.
582462306a36Sopenharmony_ci * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
582562306a36Sopenharmony_ci * audio' only. If there is any audio extension block and supported
582662306a36Sopenharmony_ci * audio format, assume at least 'basic audio' support, even if 'basic
582762306a36Sopenharmony_ci * audio' is not defined in EDID.
582862306a36Sopenharmony_ci *
582962306a36Sopenharmony_ci * Return: True if the monitor supports audio, false otherwise.
583062306a36Sopenharmony_ci */
583162306a36Sopenharmony_cibool drm_detect_monitor_audio(const struct edid *edid)
583262306a36Sopenharmony_ci{
583362306a36Sopenharmony_ci	struct drm_edid drm_edid;
583462306a36Sopenharmony_ci
583562306a36Sopenharmony_ci	return _drm_detect_monitor_audio(drm_edid_legacy_init(&drm_edid, edid));
583662306a36Sopenharmony_ci}
583762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_detect_monitor_audio);
583862306a36Sopenharmony_ci
583962306a36Sopenharmony_ci
584062306a36Sopenharmony_ci/**
584162306a36Sopenharmony_ci * drm_default_rgb_quant_range - default RGB quantization range
584262306a36Sopenharmony_ci * @mode: display mode
584362306a36Sopenharmony_ci *
584462306a36Sopenharmony_ci * Determine the default RGB quantization range for the mode,
584562306a36Sopenharmony_ci * as specified in CEA-861.
584662306a36Sopenharmony_ci *
584762306a36Sopenharmony_ci * Return: The default RGB quantization range for the mode
584862306a36Sopenharmony_ci */
584962306a36Sopenharmony_cienum hdmi_quantization_range
585062306a36Sopenharmony_cidrm_default_rgb_quant_range(const struct drm_display_mode *mode)
585162306a36Sopenharmony_ci{
585262306a36Sopenharmony_ci	/* All CEA modes other than VIC 1 use limited quantization range. */
585362306a36Sopenharmony_ci	return drm_match_cea_mode(mode) > 1 ?
585462306a36Sopenharmony_ci		HDMI_QUANTIZATION_RANGE_LIMITED :
585562306a36Sopenharmony_ci		HDMI_QUANTIZATION_RANGE_FULL;
585662306a36Sopenharmony_ci}
585762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_default_rgb_quant_range);
585862306a36Sopenharmony_ci
585962306a36Sopenharmony_ci/* CTA-861 Video Data Block (CTA VDB) */
586062306a36Sopenharmony_cistatic void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db)
586162306a36Sopenharmony_ci{
586262306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
586362306a36Sopenharmony_ci	int i, vic_index, len = cea_db_payload_len(db);
586462306a36Sopenharmony_ci	const u8 *svds = cea_db_data(db);
586562306a36Sopenharmony_ci	u8 *vics;
586662306a36Sopenharmony_ci
586762306a36Sopenharmony_ci	if (!len)
586862306a36Sopenharmony_ci		return;
586962306a36Sopenharmony_ci
587062306a36Sopenharmony_ci	/* Gracefully handle multiple VDBs, however unlikely that is */
587162306a36Sopenharmony_ci	vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL);
587262306a36Sopenharmony_ci	if (!vics)
587362306a36Sopenharmony_ci		return;
587462306a36Sopenharmony_ci
587562306a36Sopenharmony_ci	vic_index = info->vics_len;
587662306a36Sopenharmony_ci	info->vics_len += len;
587762306a36Sopenharmony_ci	info->vics = vics;
587862306a36Sopenharmony_ci
587962306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
588062306a36Sopenharmony_ci		u8 vic = svd_to_vic(svds[i]);
588162306a36Sopenharmony_ci
588262306a36Sopenharmony_ci		if (!drm_valid_cea_vic(vic))
588362306a36Sopenharmony_ci			vic = 0;
588462306a36Sopenharmony_ci
588562306a36Sopenharmony_ci		info->vics[vic_index++] = vic;
588662306a36Sopenharmony_ci	}
588762306a36Sopenharmony_ci}
588862306a36Sopenharmony_ci
588962306a36Sopenharmony_ci/*
589062306a36Sopenharmony_ci * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB.
589162306a36Sopenharmony_ci *
589262306a36Sopenharmony_ci * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed
589362306a36Sopenharmony_ci * using the VICs themselves.
589462306a36Sopenharmony_ci */
589562306a36Sopenharmony_cistatic void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map)
589662306a36Sopenharmony_ci{
589762306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
589862306a36Sopenharmony_ci	struct drm_hdmi_info *hdmi = &info->hdmi;
589962306a36Sopenharmony_ci	int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map));
590062306a36Sopenharmony_ci
590162306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
590262306a36Sopenharmony_ci		u8 vic = info->vics[i];
590362306a36Sopenharmony_ci
590462306a36Sopenharmony_ci		if (vic && y420cmdb_map & BIT_ULL(i))
590562306a36Sopenharmony_ci			bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
590662306a36Sopenharmony_ci	}
590762306a36Sopenharmony_ci}
590862306a36Sopenharmony_ci
590962306a36Sopenharmony_cistatic bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic)
591062306a36Sopenharmony_ci{
591162306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
591262306a36Sopenharmony_ci	int i;
591362306a36Sopenharmony_ci
591462306a36Sopenharmony_ci	if (!vic || !info->vics)
591562306a36Sopenharmony_ci		return false;
591662306a36Sopenharmony_ci
591762306a36Sopenharmony_ci	for (i = 0; i < info->vics_len; i++) {
591862306a36Sopenharmony_ci		if (info->vics[i] == vic)
591962306a36Sopenharmony_ci			return true;
592062306a36Sopenharmony_ci	}
592162306a36Sopenharmony_ci
592262306a36Sopenharmony_ci	return false;
592362306a36Sopenharmony_ci}
592462306a36Sopenharmony_ci
592562306a36Sopenharmony_ci/* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */
592662306a36Sopenharmony_cistatic void parse_cta_y420vdb(struct drm_connector *connector,
592762306a36Sopenharmony_ci			      const struct cea_db *db)
592862306a36Sopenharmony_ci{
592962306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
593062306a36Sopenharmony_ci	struct drm_hdmi_info *hdmi = &info->hdmi;
593162306a36Sopenharmony_ci	const u8 *svds = cea_db_data(db) + 1;
593262306a36Sopenharmony_ci	int i;
593362306a36Sopenharmony_ci
593462306a36Sopenharmony_ci	for (i = 0; i < cea_db_payload_len(db) - 1; i++) {
593562306a36Sopenharmony_ci		u8 vic = svd_to_vic(svds[i]);
593662306a36Sopenharmony_ci
593762306a36Sopenharmony_ci		if (!drm_valid_cea_vic(vic))
593862306a36Sopenharmony_ci			continue;
593962306a36Sopenharmony_ci
594062306a36Sopenharmony_ci		bitmap_set(hdmi->y420_vdb_modes, vic, 1);
594162306a36Sopenharmony_ci		info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
594262306a36Sopenharmony_ci	}
594362306a36Sopenharmony_ci}
594462306a36Sopenharmony_ci
594562306a36Sopenharmony_cistatic void drm_parse_vcdb(struct drm_connector *connector, const u8 *db)
594662306a36Sopenharmony_ci{
594762306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
594862306a36Sopenharmony_ci
594962306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] CEA VCDB 0x%02x\n",
595062306a36Sopenharmony_ci		    connector->base.id, connector->name, db[2]);
595162306a36Sopenharmony_ci
595262306a36Sopenharmony_ci	if (db[2] & EDID_CEA_VCDB_QS)
595362306a36Sopenharmony_ci		info->rgb_quant_range_selectable = true;
595462306a36Sopenharmony_ci}
595562306a36Sopenharmony_ci
595662306a36Sopenharmony_cistatic
595762306a36Sopenharmony_civoid drm_get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane)
595862306a36Sopenharmony_ci{
595962306a36Sopenharmony_ci	switch (max_frl_rate) {
596062306a36Sopenharmony_ci	case 1:
596162306a36Sopenharmony_ci		*max_lanes = 3;
596262306a36Sopenharmony_ci		*max_rate_per_lane = 3;
596362306a36Sopenharmony_ci		break;
596462306a36Sopenharmony_ci	case 2:
596562306a36Sopenharmony_ci		*max_lanes = 3;
596662306a36Sopenharmony_ci		*max_rate_per_lane = 6;
596762306a36Sopenharmony_ci		break;
596862306a36Sopenharmony_ci	case 3:
596962306a36Sopenharmony_ci		*max_lanes = 4;
597062306a36Sopenharmony_ci		*max_rate_per_lane = 6;
597162306a36Sopenharmony_ci		break;
597262306a36Sopenharmony_ci	case 4:
597362306a36Sopenharmony_ci		*max_lanes = 4;
597462306a36Sopenharmony_ci		*max_rate_per_lane = 8;
597562306a36Sopenharmony_ci		break;
597662306a36Sopenharmony_ci	case 5:
597762306a36Sopenharmony_ci		*max_lanes = 4;
597862306a36Sopenharmony_ci		*max_rate_per_lane = 10;
597962306a36Sopenharmony_ci		break;
598062306a36Sopenharmony_ci	case 6:
598162306a36Sopenharmony_ci		*max_lanes = 4;
598262306a36Sopenharmony_ci		*max_rate_per_lane = 12;
598362306a36Sopenharmony_ci		break;
598462306a36Sopenharmony_ci	case 0:
598562306a36Sopenharmony_ci	default:
598662306a36Sopenharmony_ci		*max_lanes = 0;
598762306a36Sopenharmony_ci		*max_rate_per_lane = 0;
598862306a36Sopenharmony_ci	}
598962306a36Sopenharmony_ci}
599062306a36Sopenharmony_ci
599162306a36Sopenharmony_cistatic void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
599262306a36Sopenharmony_ci					       const u8 *db)
599362306a36Sopenharmony_ci{
599462306a36Sopenharmony_ci	u8 dc_mask;
599562306a36Sopenharmony_ci	struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
599662306a36Sopenharmony_ci
599762306a36Sopenharmony_ci	dc_mask = db[7] & DRM_EDID_YCBCR420_DC_MASK;
599862306a36Sopenharmony_ci	hdmi->y420_dc_modes = dc_mask;
599962306a36Sopenharmony_ci}
600062306a36Sopenharmony_ci
600162306a36Sopenharmony_cistatic void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc,
600262306a36Sopenharmony_ci			       const u8 *hf_scds)
600362306a36Sopenharmony_ci{
600462306a36Sopenharmony_ci	hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2;
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	if (!hdmi_dsc->v_1p2)
600762306a36Sopenharmony_ci		return;
600862306a36Sopenharmony_ci
600962306a36Sopenharmony_ci	hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420;
601062306a36Sopenharmony_ci	hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP;
601162306a36Sopenharmony_ci
601262306a36Sopenharmony_ci	if (hf_scds[11] & DRM_EDID_DSC_16BPC)
601362306a36Sopenharmony_ci		hdmi_dsc->bpc_supported = 16;
601462306a36Sopenharmony_ci	else if (hf_scds[11] & DRM_EDID_DSC_12BPC)
601562306a36Sopenharmony_ci		hdmi_dsc->bpc_supported = 12;
601662306a36Sopenharmony_ci	else if (hf_scds[11] & DRM_EDID_DSC_10BPC)
601762306a36Sopenharmony_ci		hdmi_dsc->bpc_supported = 10;
601862306a36Sopenharmony_ci	else
601962306a36Sopenharmony_ci		/* Supports min 8 BPC if DSC 1.2 is supported*/
602062306a36Sopenharmony_ci		hdmi_dsc->bpc_supported = 8;
602162306a36Sopenharmony_ci
602262306a36Sopenharmony_ci	if (cea_db_payload_len(hf_scds) >= 12 && hf_scds[12]) {
602362306a36Sopenharmony_ci		u8 dsc_max_slices;
602462306a36Sopenharmony_ci		u8 dsc_max_frl_rate;
602562306a36Sopenharmony_ci
602662306a36Sopenharmony_ci		dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
602762306a36Sopenharmony_ci		drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes,
602862306a36Sopenharmony_ci				     &hdmi_dsc->max_frl_rate_per_lane);
602962306a36Sopenharmony_ci
603062306a36Sopenharmony_ci		dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES;
603162306a36Sopenharmony_ci
603262306a36Sopenharmony_ci		switch (dsc_max_slices) {
603362306a36Sopenharmony_ci		case 1:
603462306a36Sopenharmony_ci			hdmi_dsc->max_slices = 1;
603562306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 340;
603662306a36Sopenharmony_ci			break;
603762306a36Sopenharmony_ci		case 2:
603862306a36Sopenharmony_ci			hdmi_dsc->max_slices = 2;
603962306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 340;
604062306a36Sopenharmony_ci			break;
604162306a36Sopenharmony_ci		case 3:
604262306a36Sopenharmony_ci			hdmi_dsc->max_slices = 4;
604362306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 340;
604462306a36Sopenharmony_ci			break;
604562306a36Sopenharmony_ci		case 4:
604662306a36Sopenharmony_ci			hdmi_dsc->max_slices = 8;
604762306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 340;
604862306a36Sopenharmony_ci			break;
604962306a36Sopenharmony_ci		case 5:
605062306a36Sopenharmony_ci			hdmi_dsc->max_slices = 8;
605162306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 400;
605262306a36Sopenharmony_ci			break;
605362306a36Sopenharmony_ci		case 6:
605462306a36Sopenharmony_ci			hdmi_dsc->max_slices = 12;
605562306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 400;
605662306a36Sopenharmony_ci			break;
605762306a36Sopenharmony_ci		case 7:
605862306a36Sopenharmony_ci			hdmi_dsc->max_slices = 16;
605962306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 400;
606062306a36Sopenharmony_ci			break;
606162306a36Sopenharmony_ci		case 0:
606262306a36Sopenharmony_ci		default:
606362306a36Sopenharmony_ci			hdmi_dsc->max_slices = 0;
606462306a36Sopenharmony_ci			hdmi_dsc->clk_per_slice = 0;
606562306a36Sopenharmony_ci		}
606662306a36Sopenharmony_ci	}
606762306a36Sopenharmony_ci
606862306a36Sopenharmony_ci	if (cea_db_payload_len(hf_scds) >= 13 && hf_scds[13])
606962306a36Sopenharmony_ci		hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
607062306a36Sopenharmony_ci}
607162306a36Sopenharmony_ci
607262306a36Sopenharmony_ci/* Sink Capability Data Structure */
607362306a36Sopenharmony_cistatic void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
607462306a36Sopenharmony_ci				      const u8 *hf_scds)
607562306a36Sopenharmony_ci{
607662306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
607762306a36Sopenharmony_ci	struct drm_hdmi_info *hdmi = &info->hdmi;
607862306a36Sopenharmony_ci	struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
607962306a36Sopenharmony_ci	int max_tmds_clock = 0;
608062306a36Sopenharmony_ci	u8 max_frl_rate = 0;
608162306a36Sopenharmony_ci	bool dsc_support = false;
608262306a36Sopenharmony_ci
608362306a36Sopenharmony_ci	info->has_hdmi_infoframe = true;
608462306a36Sopenharmony_ci
608562306a36Sopenharmony_ci	if (hf_scds[6] & 0x80) {
608662306a36Sopenharmony_ci		hdmi->scdc.supported = true;
608762306a36Sopenharmony_ci		if (hf_scds[6] & 0x40)
608862306a36Sopenharmony_ci			hdmi->scdc.read_request = true;
608962306a36Sopenharmony_ci	}
609062306a36Sopenharmony_ci
609162306a36Sopenharmony_ci	/*
609262306a36Sopenharmony_ci	 * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz.
609362306a36Sopenharmony_ci	 * And as per the spec, three factors confirm this:
609462306a36Sopenharmony_ci	 * * Availability of a HF-VSDB block in EDID (check)
609562306a36Sopenharmony_ci	 * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check)
609662306a36Sopenharmony_ci	 * * SCDC support available (let's check)
609762306a36Sopenharmony_ci	 * Lets check it out.
609862306a36Sopenharmony_ci	 */
609962306a36Sopenharmony_ci
610062306a36Sopenharmony_ci	if (hf_scds[5]) {
610162306a36Sopenharmony_ci		struct drm_scdc *scdc = &hdmi->scdc;
610262306a36Sopenharmony_ci
610362306a36Sopenharmony_ci		/* max clock is 5000 KHz times block value */
610462306a36Sopenharmony_ci		max_tmds_clock = hf_scds[5] * 5000;
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci		if (max_tmds_clock > 340000) {
610762306a36Sopenharmony_ci			info->max_tmds_clock = max_tmds_clock;
610862306a36Sopenharmony_ci		}
610962306a36Sopenharmony_ci
611062306a36Sopenharmony_ci		if (scdc->supported) {
611162306a36Sopenharmony_ci			scdc->scrambling.supported = true;
611262306a36Sopenharmony_ci
611362306a36Sopenharmony_ci			/* Few sinks support scrambling for clocks < 340M */
611462306a36Sopenharmony_ci			if ((hf_scds[6] & 0x8))
611562306a36Sopenharmony_ci				scdc->scrambling.low_rates = true;
611662306a36Sopenharmony_ci		}
611762306a36Sopenharmony_ci	}
611862306a36Sopenharmony_ci
611962306a36Sopenharmony_ci	if (hf_scds[7]) {
612062306a36Sopenharmony_ci		max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
612162306a36Sopenharmony_ci		drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes,
612262306a36Sopenharmony_ci				     &hdmi->max_frl_rate_per_lane);
612362306a36Sopenharmony_ci	}
612462306a36Sopenharmony_ci
612562306a36Sopenharmony_ci	drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
612662306a36Sopenharmony_ci
612762306a36Sopenharmony_ci	if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) {
612862306a36Sopenharmony_ci		drm_parse_dsc_info(hdmi_dsc, hf_scds);
612962306a36Sopenharmony_ci		dsc_support = true;
613062306a36Sopenharmony_ci	}
613162306a36Sopenharmony_ci
613262306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
613362306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n",
613462306a36Sopenharmony_ci		    connector->base.id, connector->name,
613562306a36Sopenharmony_ci		    max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support));
613662306a36Sopenharmony_ci}
613762306a36Sopenharmony_ci
613862306a36Sopenharmony_cistatic void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
613962306a36Sopenharmony_ci					   const u8 *hdmi)
614062306a36Sopenharmony_ci{
614162306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
614262306a36Sopenharmony_ci	unsigned int dc_bpc = 0;
614362306a36Sopenharmony_ci
614462306a36Sopenharmony_ci	/* HDMI supports at least 8 bpc */
614562306a36Sopenharmony_ci	info->bpc = 8;
614662306a36Sopenharmony_ci
614762306a36Sopenharmony_ci	if (cea_db_payload_len(hdmi) < 6)
614862306a36Sopenharmony_ci		return;
614962306a36Sopenharmony_ci
615062306a36Sopenharmony_ci	if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
615162306a36Sopenharmony_ci		dc_bpc = 10;
615262306a36Sopenharmony_ci		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_30;
615362306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 30.\n",
615462306a36Sopenharmony_ci			    connector->base.id, connector->name);
615562306a36Sopenharmony_ci	}
615662306a36Sopenharmony_ci
615762306a36Sopenharmony_ci	if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
615862306a36Sopenharmony_ci		dc_bpc = 12;
615962306a36Sopenharmony_ci		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_36;
616062306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 36.\n",
616162306a36Sopenharmony_ci			    connector->base.id, connector->name);
616262306a36Sopenharmony_ci	}
616362306a36Sopenharmony_ci
616462306a36Sopenharmony_ci	if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
616562306a36Sopenharmony_ci		dc_bpc = 16;
616662306a36Sopenharmony_ci		info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_48;
616762306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does deep color 48.\n",
616862306a36Sopenharmony_ci			    connector->base.id, connector->name);
616962306a36Sopenharmony_ci	}
617062306a36Sopenharmony_ci
617162306a36Sopenharmony_ci	if (dc_bpc == 0) {
617262306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] No deep color support on this HDMI sink.\n",
617362306a36Sopenharmony_ci			    connector->base.id, connector->name);
617462306a36Sopenharmony_ci		return;
617562306a36Sopenharmony_ci	}
617662306a36Sopenharmony_ci
617762306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Assigning HDMI sink color depth as %d bpc.\n",
617862306a36Sopenharmony_ci		    connector->base.id, connector->name, dc_bpc);
617962306a36Sopenharmony_ci	info->bpc = dc_bpc;
618062306a36Sopenharmony_ci
618162306a36Sopenharmony_ci	/* YCRCB444 is optional according to spec. */
618262306a36Sopenharmony_ci	if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
618362306a36Sopenharmony_ci		info->edid_hdmi_ycbcr444_dc_modes = info->edid_hdmi_rgb444_dc_modes;
618462306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink does YCRCB444 in deep color.\n",
618562306a36Sopenharmony_ci			    connector->base.id, connector->name);
618662306a36Sopenharmony_ci	}
618762306a36Sopenharmony_ci
618862306a36Sopenharmony_ci	/*
618962306a36Sopenharmony_ci	 * Spec says that if any deep color mode is supported at all,
619062306a36Sopenharmony_ci	 * then deep color 36 bit must be supported.
619162306a36Sopenharmony_ci	 */
619262306a36Sopenharmony_ci	if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
619362306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI sink should do DC_36, but does not!\n",
619462306a36Sopenharmony_ci			    connector->base.id, connector->name);
619562306a36Sopenharmony_ci	}
619662306a36Sopenharmony_ci}
619762306a36Sopenharmony_ci
619862306a36Sopenharmony_ci/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
619962306a36Sopenharmony_cistatic void
620062306a36Sopenharmony_cidrm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
620162306a36Sopenharmony_ci{
620262306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
620362306a36Sopenharmony_ci	u8 len = cea_db_payload_len(db);
620462306a36Sopenharmony_ci
620562306a36Sopenharmony_ci	info->is_hdmi = true;
620662306a36Sopenharmony_ci
620762306a36Sopenharmony_ci	if (len >= 6)
620862306a36Sopenharmony_ci		info->dvi_dual = db[6] & 1;
620962306a36Sopenharmony_ci	if (len >= 7)
621062306a36Sopenharmony_ci		info->max_tmds_clock = db[7] * 5000;
621162306a36Sopenharmony_ci
621262306a36Sopenharmony_ci	/*
621362306a36Sopenharmony_ci	 * Try to infer whether the sink supports HDMI infoframes.
621462306a36Sopenharmony_ci	 *
621562306a36Sopenharmony_ci	 * HDMI infoframe support was first added in HDMI 1.4. Assume the sink
621662306a36Sopenharmony_ci	 * supports infoframes if HDMI_Video_present is set.
621762306a36Sopenharmony_ci	 */
621862306a36Sopenharmony_ci	if (len >= 8 && db[8] & BIT(5))
621962306a36Sopenharmony_ci		info->has_hdmi_infoframe = true;
622062306a36Sopenharmony_ci
622162306a36Sopenharmony_ci	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n",
622262306a36Sopenharmony_ci		    connector->base.id, connector->name,
622362306a36Sopenharmony_ci		    info->dvi_dual, info->max_tmds_clock);
622462306a36Sopenharmony_ci
622562306a36Sopenharmony_ci	drm_parse_hdmi_deep_color_info(connector, db);
622662306a36Sopenharmony_ci}
622762306a36Sopenharmony_ci
622862306a36Sopenharmony_ci/*
622962306a36Sopenharmony_ci * See EDID extension for head-mounted and specialized monitors, specified at:
623062306a36Sopenharmony_ci * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
623162306a36Sopenharmony_ci */
623262306a36Sopenharmony_cistatic void drm_parse_microsoft_vsdb(struct drm_connector *connector,
623362306a36Sopenharmony_ci				     const u8 *db)
623462306a36Sopenharmony_ci{
623562306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
623662306a36Sopenharmony_ci	u8 version = db[4];
623762306a36Sopenharmony_ci	bool desktop_usage = db[5] & BIT(6);
623862306a36Sopenharmony_ci
623962306a36Sopenharmony_ci	/* Version 1 and 2 for HMDs, version 3 flags desktop usage explicitly */
624062306a36Sopenharmony_ci	if (version == 1 || version == 2 || (version == 3 && !desktop_usage))
624162306a36Sopenharmony_ci		info->non_desktop = true;
624262306a36Sopenharmony_ci
624362306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
624462306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] HMD or specialized display VSDB version %u: 0x%02x\n",
624562306a36Sopenharmony_ci		    connector->base.id, connector->name, version, db[5]);
624662306a36Sopenharmony_ci}
624762306a36Sopenharmony_ci
624862306a36Sopenharmony_cistatic void drm_parse_cea_ext(struct drm_connector *connector,
624962306a36Sopenharmony_ci			      const struct drm_edid *drm_edid)
625062306a36Sopenharmony_ci{
625162306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
625262306a36Sopenharmony_ci	struct drm_edid_iter edid_iter;
625362306a36Sopenharmony_ci	const struct cea_db *db;
625462306a36Sopenharmony_ci	struct cea_db_iter iter;
625562306a36Sopenharmony_ci	const u8 *edid_ext;
625662306a36Sopenharmony_ci	u64 y420cmdb_map = 0;
625762306a36Sopenharmony_ci
625862306a36Sopenharmony_ci	drm_edid_iter_begin(drm_edid, &edid_iter);
625962306a36Sopenharmony_ci	drm_edid_iter_for_each(edid_ext, &edid_iter) {
626062306a36Sopenharmony_ci		if (edid_ext[0] != CEA_EXT)
626162306a36Sopenharmony_ci			continue;
626262306a36Sopenharmony_ci
626362306a36Sopenharmony_ci		if (!info->cea_rev)
626462306a36Sopenharmony_ci			info->cea_rev = edid_ext[1];
626562306a36Sopenharmony_ci
626662306a36Sopenharmony_ci		if (info->cea_rev != edid_ext[1])
626762306a36Sopenharmony_ci			drm_dbg_kms(connector->dev,
626862306a36Sopenharmony_ci				    "[CONNECTOR:%d:%s] CEA extension version mismatch %u != %u\n",
626962306a36Sopenharmony_ci				    connector->base.id, connector->name,
627062306a36Sopenharmony_ci				    info->cea_rev, edid_ext[1]);
627162306a36Sopenharmony_ci
627262306a36Sopenharmony_ci		/* The existence of a CTA extension should imply RGB support */
627362306a36Sopenharmony_ci		info->color_formats = DRM_COLOR_FORMAT_RGB444;
627462306a36Sopenharmony_ci		if (edid_ext[3] & EDID_CEA_YCRCB444)
627562306a36Sopenharmony_ci			info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
627662306a36Sopenharmony_ci		if (edid_ext[3] & EDID_CEA_YCRCB422)
627762306a36Sopenharmony_ci			info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
627862306a36Sopenharmony_ci		if (edid_ext[3] & EDID_BASIC_AUDIO)
627962306a36Sopenharmony_ci			info->has_audio = true;
628062306a36Sopenharmony_ci
628162306a36Sopenharmony_ci	}
628262306a36Sopenharmony_ci	drm_edid_iter_end(&edid_iter);
628362306a36Sopenharmony_ci
628462306a36Sopenharmony_ci	cea_db_iter_edid_begin(drm_edid, &iter);
628562306a36Sopenharmony_ci	cea_db_iter_for_each(db, &iter) {
628662306a36Sopenharmony_ci		/* FIXME: convert parsers to use struct cea_db */
628762306a36Sopenharmony_ci		const u8 *data = (const u8 *)db;
628862306a36Sopenharmony_ci
628962306a36Sopenharmony_ci		if (cea_db_is_hdmi_vsdb(db))
629062306a36Sopenharmony_ci			drm_parse_hdmi_vsdb_video(connector, data);
629162306a36Sopenharmony_ci		else if (cea_db_is_hdmi_forum_vsdb(db) ||
629262306a36Sopenharmony_ci			 cea_db_is_hdmi_forum_scdb(db))
629362306a36Sopenharmony_ci			drm_parse_hdmi_forum_scds(connector, data);
629462306a36Sopenharmony_ci		else if (cea_db_is_microsoft_vsdb(db))
629562306a36Sopenharmony_ci			drm_parse_microsoft_vsdb(connector, data);
629662306a36Sopenharmony_ci		else if (cea_db_is_y420cmdb(db))
629762306a36Sopenharmony_ci			parse_cta_y420cmdb(connector, db, &y420cmdb_map);
629862306a36Sopenharmony_ci		else if (cea_db_is_y420vdb(db))
629962306a36Sopenharmony_ci			parse_cta_y420vdb(connector, db);
630062306a36Sopenharmony_ci		else if (cea_db_is_vcdb(db))
630162306a36Sopenharmony_ci			drm_parse_vcdb(connector, data);
630262306a36Sopenharmony_ci		else if (cea_db_is_hdmi_hdr_metadata_block(db))
630362306a36Sopenharmony_ci			drm_parse_hdr_metadata_block(connector, data);
630462306a36Sopenharmony_ci		else if (cea_db_tag(db) == CTA_DB_VIDEO)
630562306a36Sopenharmony_ci			parse_cta_vdb(connector, db);
630662306a36Sopenharmony_ci		else if (cea_db_tag(db) == CTA_DB_AUDIO)
630762306a36Sopenharmony_ci			info->has_audio = true;
630862306a36Sopenharmony_ci	}
630962306a36Sopenharmony_ci	cea_db_iter_end(&iter);
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ci	if (y420cmdb_map)
631262306a36Sopenharmony_ci		update_cta_y420cmdb(connector, y420cmdb_map);
631362306a36Sopenharmony_ci}
631462306a36Sopenharmony_ci
631562306a36Sopenharmony_cistatic
631662306a36Sopenharmony_civoid get_monitor_range(const struct detailed_timing *timing, void *c)
631762306a36Sopenharmony_ci{
631862306a36Sopenharmony_ci	struct detailed_mode_closure *closure = c;
631962306a36Sopenharmony_ci	struct drm_display_info *info = &closure->connector->display_info;
632062306a36Sopenharmony_ci	struct drm_monitor_range_info *monitor_range = &info->monitor_range;
632162306a36Sopenharmony_ci	const struct detailed_non_pixel *data = &timing->data.other_data;
632262306a36Sopenharmony_ci	const struct detailed_data_monitor_range *range = &data->data.range;
632362306a36Sopenharmony_ci	const struct edid *edid = closure->drm_edid->edid;
632462306a36Sopenharmony_ci
632562306a36Sopenharmony_ci	if (!is_display_descriptor(timing, EDID_DETAIL_MONITOR_RANGE))
632662306a36Sopenharmony_ci		return;
632762306a36Sopenharmony_ci
632862306a36Sopenharmony_ci	/*
632962306a36Sopenharmony_ci	 * These limits are used to determine the VRR refresh
633062306a36Sopenharmony_ci	 * rate range. Only the "range limits only" variant
633162306a36Sopenharmony_ci	 * of the range descriptor seems to guarantee that
633262306a36Sopenharmony_ci	 * any and all timings are accepted by the sink, as
633362306a36Sopenharmony_ci	 * opposed to just timings conforming to the indicated
633462306a36Sopenharmony_ci	 * formula (GTF/GTF2/CVT). Thus other variants of the
633562306a36Sopenharmony_ci	 * range descriptor are not accepted here.
633662306a36Sopenharmony_ci	 */
633762306a36Sopenharmony_ci	if (range->flags != DRM_EDID_RANGE_LIMITS_ONLY_FLAG)
633862306a36Sopenharmony_ci		return;
633962306a36Sopenharmony_ci
634062306a36Sopenharmony_ci	monitor_range->min_vfreq = range->min_vfreq;
634162306a36Sopenharmony_ci	monitor_range->max_vfreq = range->max_vfreq;
634262306a36Sopenharmony_ci
634362306a36Sopenharmony_ci	if (edid->revision >= 4) {
634462306a36Sopenharmony_ci		if (data->pad2 & DRM_EDID_RANGE_OFFSET_MIN_VFREQ)
634562306a36Sopenharmony_ci			monitor_range->min_vfreq += 255;
634662306a36Sopenharmony_ci		if (data->pad2 & DRM_EDID_RANGE_OFFSET_MAX_VFREQ)
634762306a36Sopenharmony_ci			monitor_range->max_vfreq += 255;
634862306a36Sopenharmony_ci	}
634962306a36Sopenharmony_ci}
635062306a36Sopenharmony_ci
635162306a36Sopenharmony_cistatic void drm_get_monitor_range(struct drm_connector *connector,
635262306a36Sopenharmony_ci				  const struct drm_edid *drm_edid)
635362306a36Sopenharmony_ci{
635462306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
635562306a36Sopenharmony_ci	struct detailed_mode_closure closure = {
635662306a36Sopenharmony_ci		.connector = connector,
635762306a36Sopenharmony_ci		.drm_edid = drm_edid,
635862306a36Sopenharmony_ci	};
635962306a36Sopenharmony_ci
636062306a36Sopenharmony_ci	if (drm_edid->edid->revision < 4)
636162306a36Sopenharmony_ci		return;
636262306a36Sopenharmony_ci
636362306a36Sopenharmony_ci	if (!(drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ))
636462306a36Sopenharmony_ci		return;
636562306a36Sopenharmony_ci
636662306a36Sopenharmony_ci	drm_for_each_detailed_block(drm_edid, get_monitor_range, &closure);
636762306a36Sopenharmony_ci
636862306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
636962306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] Supported Monitor Refresh rate range is %d Hz - %d Hz\n",
637062306a36Sopenharmony_ci		    connector->base.id, connector->name,
637162306a36Sopenharmony_ci		    info->monitor_range.min_vfreq, info->monitor_range.max_vfreq);
637262306a36Sopenharmony_ci}
637362306a36Sopenharmony_ci
637462306a36Sopenharmony_cistatic void drm_parse_vesa_mso_data(struct drm_connector *connector,
637562306a36Sopenharmony_ci				    const struct displayid_block *block)
637662306a36Sopenharmony_ci{
637762306a36Sopenharmony_ci	struct displayid_vesa_vendor_specific_block *vesa =
637862306a36Sopenharmony_ci		(struct displayid_vesa_vendor_specific_block *)block;
637962306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
638062306a36Sopenharmony_ci
638162306a36Sopenharmony_ci	if (block->num_bytes < 3) {
638262306a36Sopenharmony_ci		drm_dbg_kms(connector->dev,
638362306a36Sopenharmony_ci			    "[CONNECTOR:%d:%s] Unexpected vendor block size %u\n",
638462306a36Sopenharmony_ci			    connector->base.id, connector->name, block->num_bytes);
638562306a36Sopenharmony_ci		return;
638662306a36Sopenharmony_ci	}
638762306a36Sopenharmony_ci
638862306a36Sopenharmony_ci	if (oui(vesa->oui[0], vesa->oui[1], vesa->oui[2]) != VESA_IEEE_OUI)
638962306a36Sopenharmony_ci		return;
639062306a36Sopenharmony_ci
639162306a36Sopenharmony_ci	if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) {
639262306a36Sopenharmony_ci		drm_dbg_kms(connector->dev,
639362306a36Sopenharmony_ci			    "[CONNECTOR:%d:%s] Unexpected VESA vendor block size\n",
639462306a36Sopenharmony_ci			    connector->base.id, connector->name);
639562306a36Sopenharmony_ci		return;
639662306a36Sopenharmony_ci	}
639762306a36Sopenharmony_ci
639862306a36Sopenharmony_ci	switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) {
639962306a36Sopenharmony_ci	default:
640062306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved MSO mode value\n",
640162306a36Sopenharmony_ci			    connector->base.id, connector->name);
640262306a36Sopenharmony_ci		fallthrough;
640362306a36Sopenharmony_ci	case 0:
640462306a36Sopenharmony_ci		info->mso_stream_count = 0;
640562306a36Sopenharmony_ci		break;
640662306a36Sopenharmony_ci	case 1:
640762306a36Sopenharmony_ci		info->mso_stream_count = 2; /* 2 or 4 links */
640862306a36Sopenharmony_ci		break;
640962306a36Sopenharmony_ci	case 2:
641062306a36Sopenharmony_ci		info->mso_stream_count = 4; /* 4 links */
641162306a36Sopenharmony_ci		break;
641262306a36Sopenharmony_ci	}
641362306a36Sopenharmony_ci
641462306a36Sopenharmony_ci	if (!info->mso_stream_count) {
641562306a36Sopenharmony_ci		info->mso_pixel_overlap = 0;
641662306a36Sopenharmony_ci		return;
641762306a36Sopenharmony_ci	}
641862306a36Sopenharmony_ci
641962306a36Sopenharmony_ci	info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso);
642062306a36Sopenharmony_ci	if (info->mso_pixel_overlap > 8) {
642162306a36Sopenharmony_ci		drm_dbg_kms(connector->dev,
642262306a36Sopenharmony_ci			    "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n",
642362306a36Sopenharmony_ci			    connector->base.id, connector->name,
642462306a36Sopenharmony_ci			    info->mso_pixel_overlap);
642562306a36Sopenharmony_ci		info->mso_pixel_overlap = 8;
642662306a36Sopenharmony_ci	}
642762306a36Sopenharmony_ci
642862306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
642962306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n",
643062306a36Sopenharmony_ci		    connector->base.id, connector->name,
643162306a36Sopenharmony_ci		    info->mso_stream_count, info->mso_pixel_overlap);
643262306a36Sopenharmony_ci}
643362306a36Sopenharmony_ci
643462306a36Sopenharmony_cistatic void drm_update_mso(struct drm_connector *connector,
643562306a36Sopenharmony_ci			   const struct drm_edid *drm_edid)
643662306a36Sopenharmony_ci{
643762306a36Sopenharmony_ci	const struct displayid_block *block;
643862306a36Sopenharmony_ci	struct displayid_iter iter;
643962306a36Sopenharmony_ci
644062306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter);
644162306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter) {
644262306a36Sopenharmony_ci		if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
644362306a36Sopenharmony_ci			drm_parse_vesa_mso_data(connector, block);
644462306a36Sopenharmony_ci	}
644562306a36Sopenharmony_ci	displayid_iter_end(&iter);
644662306a36Sopenharmony_ci}
644762306a36Sopenharmony_ci
644862306a36Sopenharmony_ci/* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
644962306a36Sopenharmony_ci * all of the values which would have been set from EDID
645062306a36Sopenharmony_ci */
645162306a36Sopenharmony_cistatic void drm_reset_display_info(struct drm_connector *connector)
645262306a36Sopenharmony_ci{
645362306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
645462306a36Sopenharmony_ci
645562306a36Sopenharmony_ci	info->width_mm = 0;
645662306a36Sopenharmony_ci	info->height_mm = 0;
645762306a36Sopenharmony_ci
645862306a36Sopenharmony_ci	info->bpc = 0;
645962306a36Sopenharmony_ci	info->color_formats = 0;
646062306a36Sopenharmony_ci	info->cea_rev = 0;
646162306a36Sopenharmony_ci	info->max_tmds_clock = 0;
646262306a36Sopenharmony_ci	info->dvi_dual = false;
646362306a36Sopenharmony_ci	info->is_hdmi = false;
646462306a36Sopenharmony_ci	info->has_audio = false;
646562306a36Sopenharmony_ci	info->has_hdmi_infoframe = false;
646662306a36Sopenharmony_ci	info->rgb_quant_range_selectable = false;
646762306a36Sopenharmony_ci	memset(&info->hdmi, 0, sizeof(info->hdmi));
646862306a36Sopenharmony_ci
646962306a36Sopenharmony_ci	info->edid_hdmi_rgb444_dc_modes = 0;
647062306a36Sopenharmony_ci	info->edid_hdmi_ycbcr444_dc_modes = 0;
647162306a36Sopenharmony_ci
647262306a36Sopenharmony_ci	info->non_desktop = 0;
647362306a36Sopenharmony_ci	memset(&info->monitor_range, 0, sizeof(info->monitor_range));
647462306a36Sopenharmony_ci	memset(&info->luminance_range, 0, sizeof(info->luminance_range));
647562306a36Sopenharmony_ci
647662306a36Sopenharmony_ci	info->mso_stream_count = 0;
647762306a36Sopenharmony_ci	info->mso_pixel_overlap = 0;
647862306a36Sopenharmony_ci	info->max_dsc_bpp = 0;
647962306a36Sopenharmony_ci
648062306a36Sopenharmony_ci	kfree(info->vics);
648162306a36Sopenharmony_ci	info->vics = NULL;
648262306a36Sopenharmony_ci	info->vics_len = 0;
648362306a36Sopenharmony_ci
648462306a36Sopenharmony_ci	info->quirks = 0;
648562306a36Sopenharmony_ci}
648662306a36Sopenharmony_ci
648762306a36Sopenharmony_cistatic void update_displayid_info(struct drm_connector *connector,
648862306a36Sopenharmony_ci				  const struct drm_edid *drm_edid)
648962306a36Sopenharmony_ci{
649062306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
649162306a36Sopenharmony_ci	const struct displayid_block *block;
649262306a36Sopenharmony_ci	struct displayid_iter iter;
649362306a36Sopenharmony_ci
649462306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter);
649562306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter) {
649662306a36Sopenharmony_ci		if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
649762306a36Sopenharmony_ci		    (displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_VR ||
649862306a36Sopenharmony_ci		     displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_AR))
649962306a36Sopenharmony_ci			info->non_desktop = true;
650062306a36Sopenharmony_ci
650162306a36Sopenharmony_ci		/*
650262306a36Sopenharmony_ci		 * We're only interested in the base section here, no need to
650362306a36Sopenharmony_ci		 * iterate further.
650462306a36Sopenharmony_ci		 */
650562306a36Sopenharmony_ci		break;
650662306a36Sopenharmony_ci	}
650762306a36Sopenharmony_ci	displayid_iter_end(&iter);
650862306a36Sopenharmony_ci}
650962306a36Sopenharmony_ci
651062306a36Sopenharmony_cistatic void update_display_info(struct drm_connector *connector,
651162306a36Sopenharmony_ci				const struct drm_edid *drm_edid)
651262306a36Sopenharmony_ci{
651362306a36Sopenharmony_ci	struct drm_display_info *info = &connector->display_info;
651462306a36Sopenharmony_ci	const struct edid *edid;
651562306a36Sopenharmony_ci
651662306a36Sopenharmony_ci	drm_reset_display_info(connector);
651762306a36Sopenharmony_ci	clear_eld(connector);
651862306a36Sopenharmony_ci
651962306a36Sopenharmony_ci	if (!drm_edid)
652062306a36Sopenharmony_ci		return;
652162306a36Sopenharmony_ci
652262306a36Sopenharmony_ci	edid = drm_edid->edid;
652362306a36Sopenharmony_ci
652462306a36Sopenharmony_ci	info->quirks = edid_get_quirks(drm_edid);
652562306a36Sopenharmony_ci
652662306a36Sopenharmony_ci	info->width_mm = edid->width_cm * 10;
652762306a36Sopenharmony_ci	info->height_mm = edid->height_cm * 10;
652862306a36Sopenharmony_ci
652962306a36Sopenharmony_ci	drm_get_monitor_range(connector, drm_edid);
653062306a36Sopenharmony_ci
653162306a36Sopenharmony_ci	if (edid->revision < 3)
653262306a36Sopenharmony_ci		goto out;
653362306a36Sopenharmony_ci
653462306a36Sopenharmony_ci	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
653562306a36Sopenharmony_ci		goto out;
653662306a36Sopenharmony_ci
653762306a36Sopenharmony_ci	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
653862306a36Sopenharmony_ci	drm_parse_cea_ext(connector, drm_edid);
653962306a36Sopenharmony_ci
654062306a36Sopenharmony_ci	update_displayid_info(connector, drm_edid);
654162306a36Sopenharmony_ci
654262306a36Sopenharmony_ci	/*
654362306a36Sopenharmony_ci	 * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
654462306a36Sopenharmony_ci	 *
654562306a36Sopenharmony_ci	 * For such displays, the DFP spec 1.0, section 3.10 "EDID support"
654662306a36Sopenharmony_ci	 * tells us to assume 8 bpc color depth if the EDID doesn't have
654762306a36Sopenharmony_ci	 * extensions which tell otherwise.
654862306a36Sopenharmony_ci	 */
654962306a36Sopenharmony_ci	if (info->bpc == 0 && edid->revision == 3 &&
655062306a36Sopenharmony_ci	    edid->input & DRM_EDID_DIGITAL_DFP_1_X) {
655162306a36Sopenharmony_ci		info->bpc = 8;
655262306a36Sopenharmony_ci		drm_dbg_kms(connector->dev,
655362306a36Sopenharmony_ci			    "[CONNECTOR:%d:%s] Assigning DFP sink color depth as %d bpc.\n",
655462306a36Sopenharmony_ci			    connector->base.id, connector->name, info->bpc);
655562306a36Sopenharmony_ci	}
655662306a36Sopenharmony_ci
655762306a36Sopenharmony_ci	/* Only defined for 1.4 with digital displays */
655862306a36Sopenharmony_ci	if (edid->revision < 4)
655962306a36Sopenharmony_ci		goto out;
656062306a36Sopenharmony_ci
656162306a36Sopenharmony_ci	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
656262306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_6:
656362306a36Sopenharmony_ci		info->bpc = 6;
656462306a36Sopenharmony_ci		break;
656562306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_8:
656662306a36Sopenharmony_ci		info->bpc = 8;
656762306a36Sopenharmony_ci		break;
656862306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_10:
656962306a36Sopenharmony_ci		info->bpc = 10;
657062306a36Sopenharmony_ci		break;
657162306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_12:
657262306a36Sopenharmony_ci		info->bpc = 12;
657362306a36Sopenharmony_ci		break;
657462306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_14:
657562306a36Sopenharmony_ci		info->bpc = 14;
657662306a36Sopenharmony_ci		break;
657762306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_16:
657862306a36Sopenharmony_ci		info->bpc = 16;
657962306a36Sopenharmony_ci		break;
658062306a36Sopenharmony_ci	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
658162306a36Sopenharmony_ci	default:
658262306a36Sopenharmony_ci		info->bpc = 0;
658362306a36Sopenharmony_ci		break;
658462306a36Sopenharmony_ci	}
658562306a36Sopenharmony_ci
658662306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
658762306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
658862306a36Sopenharmony_ci		    connector->base.id, connector->name, info->bpc);
658962306a36Sopenharmony_ci
659062306a36Sopenharmony_ci	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
659162306a36Sopenharmony_ci		info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
659262306a36Sopenharmony_ci	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
659362306a36Sopenharmony_ci		info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
659462306a36Sopenharmony_ci
659562306a36Sopenharmony_ci	drm_update_mso(connector, drm_edid);
659662306a36Sopenharmony_ci
659762306a36Sopenharmony_ciout:
659862306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_NON_DESKTOP) {
659962306a36Sopenharmony_ci		drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n",
660062306a36Sopenharmony_ci			    connector->base.id, connector->name,
660162306a36Sopenharmony_ci			    info->non_desktop ? " (redundant quirk)" : "");
660262306a36Sopenharmony_ci		info->non_desktop = true;
660362306a36Sopenharmony_ci	}
660462306a36Sopenharmony_ci
660562306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP)
660662306a36Sopenharmony_ci		info->max_dsc_bpp = 15;
660762306a36Sopenharmony_ci
660862306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_FORCE_6BPC)
660962306a36Sopenharmony_ci		info->bpc = 6;
661062306a36Sopenharmony_ci
661162306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_FORCE_8BPC)
661262306a36Sopenharmony_ci		info->bpc = 8;
661362306a36Sopenharmony_ci
661462306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_FORCE_10BPC)
661562306a36Sopenharmony_ci		info->bpc = 10;
661662306a36Sopenharmony_ci
661762306a36Sopenharmony_ci	if (info->quirks & EDID_QUIRK_FORCE_12BPC)
661862306a36Sopenharmony_ci		info->bpc = 12;
661962306a36Sopenharmony_ci
662062306a36Sopenharmony_ci	/* Depends on info->cea_rev set by drm_parse_cea_ext() above */
662162306a36Sopenharmony_ci	drm_edid_to_eld(connector, drm_edid);
662262306a36Sopenharmony_ci}
662362306a36Sopenharmony_ci
662462306a36Sopenharmony_cistatic struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
662562306a36Sopenharmony_ci							    struct displayid_detailed_timings_1 *timings,
662662306a36Sopenharmony_ci							    bool type_7)
662762306a36Sopenharmony_ci{
662862306a36Sopenharmony_ci	struct drm_display_mode *mode;
662962306a36Sopenharmony_ci	unsigned pixel_clock = (timings->pixel_clock[0] |
663062306a36Sopenharmony_ci				(timings->pixel_clock[1] << 8) |
663162306a36Sopenharmony_ci				(timings->pixel_clock[2] << 16)) + 1;
663262306a36Sopenharmony_ci	unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
663362306a36Sopenharmony_ci	unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
663462306a36Sopenharmony_ci	unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
663562306a36Sopenharmony_ci	unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1;
663662306a36Sopenharmony_ci	unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1;
663762306a36Sopenharmony_ci	unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1;
663862306a36Sopenharmony_ci	unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1;
663962306a36Sopenharmony_ci	unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
664062306a36Sopenharmony_ci	bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
664162306a36Sopenharmony_ci	bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
664262306a36Sopenharmony_ci
664362306a36Sopenharmony_ci	mode = drm_mode_create(dev);
664462306a36Sopenharmony_ci	if (!mode)
664562306a36Sopenharmony_ci		return NULL;
664662306a36Sopenharmony_ci
664762306a36Sopenharmony_ci	/* resolution is kHz for type VII, and 10 kHz for type I */
664862306a36Sopenharmony_ci	mode->clock = type_7 ? pixel_clock : pixel_clock * 10;
664962306a36Sopenharmony_ci	mode->hdisplay = hactive;
665062306a36Sopenharmony_ci	mode->hsync_start = mode->hdisplay + hsync;
665162306a36Sopenharmony_ci	mode->hsync_end = mode->hsync_start + hsync_width;
665262306a36Sopenharmony_ci	mode->htotal = mode->hdisplay + hblank;
665362306a36Sopenharmony_ci
665462306a36Sopenharmony_ci	mode->vdisplay = vactive;
665562306a36Sopenharmony_ci	mode->vsync_start = mode->vdisplay + vsync;
665662306a36Sopenharmony_ci	mode->vsync_end = mode->vsync_start + vsync_width;
665762306a36Sopenharmony_ci	mode->vtotal = mode->vdisplay + vblank;
665862306a36Sopenharmony_ci
665962306a36Sopenharmony_ci	mode->flags = 0;
666062306a36Sopenharmony_ci	mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
666162306a36Sopenharmony_ci	mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
666262306a36Sopenharmony_ci	mode->type = DRM_MODE_TYPE_DRIVER;
666362306a36Sopenharmony_ci
666462306a36Sopenharmony_ci	if (timings->flags & 0x80)
666562306a36Sopenharmony_ci		mode->type |= DRM_MODE_TYPE_PREFERRED;
666662306a36Sopenharmony_ci	drm_mode_set_name(mode);
666762306a36Sopenharmony_ci
666862306a36Sopenharmony_ci	return mode;
666962306a36Sopenharmony_ci}
667062306a36Sopenharmony_ci
667162306a36Sopenharmony_cistatic int add_displayid_detailed_1_modes(struct drm_connector *connector,
667262306a36Sopenharmony_ci					  const struct displayid_block *block)
667362306a36Sopenharmony_ci{
667462306a36Sopenharmony_ci	struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block;
667562306a36Sopenharmony_ci	int i;
667662306a36Sopenharmony_ci	int num_timings;
667762306a36Sopenharmony_ci	struct drm_display_mode *newmode;
667862306a36Sopenharmony_ci	int num_modes = 0;
667962306a36Sopenharmony_ci	bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING;
668062306a36Sopenharmony_ci	/* blocks must be multiple of 20 bytes length */
668162306a36Sopenharmony_ci	if (block->num_bytes % 20)
668262306a36Sopenharmony_ci		return 0;
668362306a36Sopenharmony_ci
668462306a36Sopenharmony_ci	num_timings = block->num_bytes / 20;
668562306a36Sopenharmony_ci	for (i = 0; i < num_timings; i++) {
668662306a36Sopenharmony_ci		struct displayid_detailed_timings_1 *timings = &det->timings[i];
668762306a36Sopenharmony_ci
668862306a36Sopenharmony_ci		newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7);
668962306a36Sopenharmony_ci		if (!newmode)
669062306a36Sopenharmony_ci			continue;
669162306a36Sopenharmony_ci
669262306a36Sopenharmony_ci		drm_mode_probed_add(connector, newmode);
669362306a36Sopenharmony_ci		num_modes++;
669462306a36Sopenharmony_ci	}
669562306a36Sopenharmony_ci	return num_modes;
669662306a36Sopenharmony_ci}
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_cistatic int add_displayid_detailed_modes(struct drm_connector *connector,
669962306a36Sopenharmony_ci					const struct drm_edid *drm_edid)
670062306a36Sopenharmony_ci{
670162306a36Sopenharmony_ci	const struct displayid_block *block;
670262306a36Sopenharmony_ci	struct displayid_iter iter;
670362306a36Sopenharmony_ci	int num_modes = 0;
670462306a36Sopenharmony_ci
670562306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter);
670662306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter) {
670762306a36Sopenharmony_ci		if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
670862306a36Sopenharmony_ci		    block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
670962306a36Sopenharmony_ci			num_modes += add_displayid_detailed_1_modes(connector, block);
671062306a36Sopenharmony_ci	}
671162306a36Sopenharmony_ci	displayid_iter_end(&iter);
671262306a36Sopenharmony_ci
671362306a36Sopenharmony_ci	return num_modes;
671462306a36Sopenharmony_ci}
671562306a36Sopenharmony_ci
671662306a36Sopenharmony_cistatic int _drm_edid_connector_add_modes(struct drm_connector *connector,
671762306a36Sopenharmony_ci					 const struct drm_edid *drm_edid)
671862306a36Sopenharmony_ci{
671962306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
672062306a36Sopenharmony_ci	int num_modes = 0;
672162306a36Sopenharmony_ci
672262306a36Sopenharmony_ci	if (!drm_edid)
672362306a36Sopenharmony_ci		return 0;
672462306a36Sopenharmony_ci
672562306a36Sopenharmony_ci	/*
672662306a36Sopenharmony_ci	 * EDID spec says modes should be preferred in this order:
672762306a36Sopenharmony_ci	 * - preferred detailed mode
672862306a36Sopenharmony_ci	 * - other detailed modes from base block
672962306a36Sopenharmony_ci	 * - detailed modes from extension blocks
673062306a36Sopenharmony_ci	 * - CVT 3-byte code modes
673162306a36Sopenharmony_ci	 * - standard timing codes
673262306a36Sopenharmony_ci	 * - established timing codes
673362306a36Sopenharmony_ci	 * - modes inferred from GTF or CVT range information
673462306a36Sopenharmony_ci	 *
673562306a36Sopenharmony_ci	 * We get this pretty much right.
673662306a36Sopenharmony_ci	 *
673762306a36Sopenharmony_ci	 * XXX order for additional mode types in extension blocks?
673862306a36Sopenharmony_ci	 */
673962306a36Sopenharmony_ci	num_modes += add_detailed_modes(connector, drm_edid);
674062306a36Sopenharmony_ci	num_modes += add_cvt_modes(connector, drm_edid);
674162306a36Sopenharmony_ci	num_modes += add_standard_modes(connector, drm_edid);
674262306a36Sopenharmony_ci	num_modes += add_established_modes(connector, drm_edid);
674362306a36Sopenharmony_ci	num_modes += add_cea_modes(connector, drm_edid);
674462306a36Sopenharmony_ci	num_modes += add_alternate_cea_modes(connector, drm_edid);
674562306a36Sopenharmony_ci	num_modes += add_displayid_detailed_modes(connector, drm_edid);
674662306a36Sopenharmony_ci	if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ)
674762306a36Sopenharmony_ci		num_modes += add_inferred_modes(connector, drm_edid);
674862306a36Sopenharmony_ci
674962306a36Sopenharmony_ci	if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
675062306a36Sopenharmony_ci		edid_fixup_preferred(connector);
675162306a36Sopenharmony_ci
675262306a36Sopenharmony_ci	return num_modes;
675362306a36Sopenharmony_ci}
675462306a36Sopenharmony_ci
675562306a36Sopenharmony_cistatic void _drm_update_tile_info(struct drm_connector *connector,
675662306a36Sopenharmony_ci				  const struct drm_edid *drm_edid);
675762306a36Sopenharmony_ci
675862306a36Sopenharmony_cistatic int _drm_edid_connector_property_update(struct drm_connector *connector,
675962306a36Sopenharmony_ci					       const struct drm_edid *drm_edid)
676062306a36Sopenharmony_ci{
676162306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
676262306a36Sopenharmony_ci	int ret;
676362306a36Sopenharmony_ci
676462306a36Sopenharmony_ci	if (connector->edid_blob_ptr) {
676562306a36Sopenharmony_ci		const struct edid *old_edid = connector->edid_blob_ptr->data;
676662306a36Sopenharmony_ci
676762306a36Sopenharmony_ci		if (old_edid) {
676862306a36Sopenharmony_ci			if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) {
676962306a36Sopenharmony_ci				connector->epoch_counter++;
677062306a36Sopenharmony_ci				drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n",
677162306a36Sopenharmony_ci					    connector->base.id, connector->name,
677262306a36Sopenharmony_ci					    connector->epoch_counter);
677362306a36Sopenharmony_ci			}
677462306a36Sopenharmony_ci		}
677562306a36Sopenharmony_ci	}
677662306a36Sopenharmony_ci
677762306a36Sopenharmony_ci	ret = drm_property_replace_global_blob(dev,
677862306a36Sopenharmony_ci					       &connector->edid_blob_ptr,
677962306a36Sopenharmony_ci					       drm_edid ? drm_edid->size : 0,
678062306a36Sopenharmony_ci					       drm_edid ? drm_edid->edid : NULL,
678162306a36Sopenharmony_ci					       &connector->base,
678262306a36Sopenharmony_ci					       dev->mode_config.edid_property);
678362306a36Sopenharmony_ci	if (ret) {
678462306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID property update failed (%d)\n",
678562306a36Sopenharmony_ci			    connector->base.id, connector->name, ret);
678662306a36Sopenharmony_ci		goto out;
678762306a36Sopenharmony_ci	}
678862306a36Sopenharmony_ci
678962306a36Sopenharmony_ci	ret = drm_object_property_set_value(&connector->base,
679062306a36Sopenharmony_ci					    dev->mode_config.non_desktop_property,
679162306a36Sopenharmony_ci					    connector->display_info.non_desktop);
679262306a36Sopenharmony_ci	if (ret) {
679362306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Non-desktop property update failed (%d)\n",
679462306a36Sopenharmony_ci			    connector->base.id, connector->name, ret);
679562306a36Sopenharmony_ci		goto out;
679662306a36Sopenharmony_ci	}
679762306a36Sopenharmony_ci
679862306a36Sopenharmony_ci	ret = drm_connector_set_tile_property(connector);
679962306a36Sopenharmony_ci	if (ret) {
680062306a36Sopenharmony_ci		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Tile property update failed (%d)\n",
680162306a36Sopenharmony_ci			    connector->base.id, connector->name, ret);
680262306a36Sopenharmony_ci		goto out;
680362306a36Sopenharmony_ci	}
680462306a36Sopenharmony_ci
680562306a36Sopenharmony_ciout:
680662306a36Sopenharmony_ci	return ret;
680762306a36Sopenharmony_ci}
680862306a36Sopenharmony_ci
680962306a36Sopenharmony_ci/**
681062306a36Sopenharmony_ci * drm_edid_connector_update - Update connector information from EDID
681162306a36Sopenharmony_ci * @connector: Connector
681262306a36Sopenharmony_ci * @drm_edid: EDID
681362306a36Sopenharmony_ci *
681462306a36Sopenharmony_ci * Update the connector display info, ELD, HDR metadata, relevant properties,
681562306a36Sopenharmony_ci * etc. from the passed in EDID.
681662306a36Sopenharmony_ci *
681762306a36Sopenharmony_ci * If EDID is NULL, reset the information.
681862306a36Sopenharmony_ci *
681962306a36Sopenharmony_ci * Must be called before calling drm_edid_connector_add_modes().
682062306a36Sopenharmony_ci *
682162306a36Sopenharmony_ci * Return: 0 on success, negative error on errors.
682262306a36Sopenharmony_ci */
682362306a36Sopenharmony_ciint drm_edid_connector_update(struct drm_connector *connector,
682462306a36Sopenharmony_ci			      const struct drm_edid *drm_edid)
682562306a36Sopenharmony_ci{
682662306a36Sopenharmony_ci	update_display_info(connector, drm_edid);
682762306a36Sopenharmony_ci
682862306a36Sopenharmony_ci	_drm_update_tile_info(connector, drm_edid);
682962306a36Sopenharmony_ci
683062306a36Sopenharmony_ci	return _drm_edid_connector_property_update(connector, drm_edid);
683162306a36Sopenharmony_ci}
683262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_connector_update);
683362306a36Sopenharmony_ci
683462306a36Sopenharmony_ci/**
683562306a36Sopenharmony_ci * drm_edid_connector_add_modes - Update probed modes from the EDID property
683662306a36Sopenharmony_ci * @connector: Connector
683762306a36Sopenharmony_ci *
683862306a36Sopenharmony_ci * Add the modes from the previously updated EDID property to the connector
683962306a36Sopenharmony_ci * probed modes list.
684062306a36Sopenharmony_ci *
684162306a36Sopenharmony_ci * drm_edid_connector_update() must have been called before this to update the
684262306a36Sopenharmony_ci * EDID property.
684362306a36Sopenharmony_ci *
684462306a36Sopenharmony_ci * Return: The number of modes added, or 0 if we couldn't find any.
684562306a36Sopenharmony_ci */
684662306a36Sopenharmony_ciint drm_edid_connector_add_modes(struct drm_connector *connector)
684762306a36Sopenharmony_ci{
684862306a36Sopenharmony_ci	const struct drm_edid *drm_edid = NULL;
684962306a36Sopenharmony_ci	int count;
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_ci	if (connector->edid_blob_ptr)
685262306a36Sopenharmony_ci		drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data,
685362306a36Sopenharmony_ci					  connector->edid_blob_ptr->length);
685462306a36Sopenharmony_ci
685562306a36Sopenharmony_ci	count = _drm_edid_connector_add_modes(connector, drm_edid);
685662306a36Sopenharmony_ci
685762306a36Sopenharmony_ci	drm_edid_free(drm_edid);
685862306a36Sopenharmony_ci
685962306a36Sopenharmony_ci	return count;
686062306a36Sopenharmony_ci}
686162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_edid_connector_add_modes);
686262306a36Sopenharmony_ci
686362306a36Sopenharmony_ci/**
686462306a36Sopenharmony_ci * drm_connector_update_edid_property - update the edid property of a connector
686562306a36Sopenharmony_ci * @connector: drm connector
686662306a36Sopenharmony_ci * @edid: new value of the edid property
686762306a36Sopenharmony_ci *
686862306a36Sopenharmony_ci * This function creates a new blob modeset object and assigns its id to the
686962306a36Sopenharmony_ci * connector's edid property.
687062306a36Sopenharmony_ci * Since we also parse tile information from EDID's displayID block, we also
687162306a36Sopenharmony_ci * set the connector's tile property here. See drm_connector_set_tile_property()
687262306a36Sopenharmony_ci * for more details.
687362306a36Sopenharmony_ci *
687462306a36Sopenharmony_ci * This function is deprecated. Use drm_edid_connector_update() instead.
687562306a36Sopenharmony_ci *
687662306a36Sopenharmony_ci * Returns:
687762306a36Sopenharmony_ci * Zero on success, negative errno on failure.
687862306a36Sopenharmony_ci */
687962306a36Sopenharmony_ciint drm_connector_update_edid_property(struct drm_connector *connector,
688062306a36Sopenharmony_ci				       const struct edid *edid)
688162306a36Sopenharmony_ci{
688262306a36Sopenharmony_ci	struct drm_edid drm_edid;
688362306a36Sopenharmony_ci
688462306a36Sopenharmony_ci	return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid));
688562306a36Sopenharmony_ci}
688662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_connector_update_edid_property);
688762306a36Sopenharmony_ci
688862306a36Sopenharmony_ci/**
688962306a36Sopenharmony_ci * drm_add_edid_modes - add modes from EDID data, if available
689062306a36Sopenharmony_ci * @connector: connector we're probing
689162306a36Sopenharmony_ci * @edid: EDID data
689262306a36Sopenharmony_ci *
689362306a36Sopenharmony_ci * Add the specified modes to the connector's mode list. Also fills out the
689462306a36Sopenharmony_ci * &drm_display_info structure and ELD in @connector with any information which
689562306a36Sopenharmony_ci * can be derived from the edid.
689662306a36Sopenharmony_ci *
689762306a36Sopenharmony_ci * This function is deprecated. Use drm_edid_connector_add_modes() instead.
689862306a36Sopenharmony_ci *
689962306a36Sopenharmony_ci * Return: The number of modes added or 0 if we couldn't find any.
690062306a36Sopenharmony_ci */
690162306a36Sopenharmony_ciint drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
690262306a36Sopenharmony_ci{
690362306a36Sopenharmony_ci	struct drm_edid _drm_edid;
690462306a36Sopenharmony_ci	const struct drm_edid *drm_edid;
690562306a36Sopenharmony_ci
690662306a36Sopenharmony_ci	if (edid && !drm_edid_is_valid(edid)) {
690762306a36Sopenharmony_ci		drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n",
690862306a36Sopenharmony_ci			 connector->base.id, connector->name);
690962306a36Sopenharmony_ci		edid = NULL;
691062306a36Sopenharmony_ci	}
691162306a36Sopenharmony_ci
691262306a36Sopenharmony_ci	drm_edid = drm_edid_legacy_init(&_drm_edid, edid);
691362306a36Sopenharmony_ci
691462306a36Sopenharmony_ci	update_display_info(connector, drm_edid);
691562306a36Sopenharmony_ci
691662306a36Sopenharmony_ci	return _drm_edid_connector_add_modes(connector, drm_edid);
691762306a36Sopenharmony_ci}
691862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_add_edid_modes);
691962306a36Sopenharmony_ci
692062306a36Sopenharmony_ci/**
692162306a36Sopenharmony_ci * drm_add_modes_noedid - add modes for the connectors without EDID
692262306a36Sopenharmony_ci * @connector: connector we're probing
692362306a36Sopenharmony_ci * @hdisplay: the horizontal display limit
692462306a36Sopenharmony_ci * @vdisplay: the vertical display limit
692562306a36Sopenharmony_ci *
692662306a36Sopenharmony_ci * Add the specified modes to the connector's mode list. Only when the
692762306a36Sopenharmony_ci * hdisplay/vdisplay is not beyond the given limit, it will be added.
692862306a36Sopenharmony_ci *
692962306a36Sopenharmony_ci * Return: The number of modes added or 0 if we couldn't find any.
693062306a36Sopenharmony_ci */
693162306a36Sopenharmony_ciint drm_add_modes_noedid(struct drm_connector *connector,
693262306a36Sopenharmony_ci			int hdisplay, int vdisplay)
693362306a36Sopenharmony_ci{
693462306a36Sopenharmony_ci	int i, count, num_modes = 0;
693562306a36Sopenharmony_ci	struct drm_display_mode *mode;
693662306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
693762306a36Sopenharmony_ci
693862306a36Sopenharmony_ci	count = ARRAY_SIZE(drm_dmt_modes);
693962306a36Sopenharmony_ci	if (hdisplay < 0)
694062306a36Sopenharmony_ci		hdisplay = 0;
694162306a36Sopenharmony_ci	if (vdisplay < 0)
694262306a36Sopenharmony_ci		vdisplay = 0;
694362306a36Sopenharmony_ci
694462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
694562306a36Sopenharmony_ci		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
694662306a36Sopenharmony_ci
694762306a36Sopenharmony_ci		if (hdisplay && vdisplay) {
694862306a36Sopenharmony_ci			/*
694962306a36Sopenharmony_ci			 * Only when two are valid, they will be used to check
695062306a36Sopenharmony_ci			 * whether the mode should be added to the mode list of
695162306a36Sopenharmony_ci			 * the connector.
695262306a36Sopenharmony_ci			 */
695362306a36Sopenharmony_ci			if (ptr->hdisplay > hdisplay ||
695462306a36Sopenharmony_ci					ptr->vdisplay > vdisplay)
695562306a36Sopenharmony_ci				continue;
695662306a36Sopenharmony_ci		}
695762306a36Sopenharmony_ci		if (drm_mode_vrefresh(ptr) > 61)
695862306a36Sopenharmony_ci			continue;
695962306a36Sopenharmony_ci		mode = drm_mode_duplicate(dev, ptr);
696062306a36Sopenharmony_ci		if (mode) {
696162306a36Sopenharmony_ci			drm_mode_probed_add(connector, mode);
696262306a36Sopenharmony_ci			num_modes++;
696362306a36Sopenharmony_ci		}
696462306a36Sopenharmony_ci	}
696562306a36Sopenharmony_ci	return num_modes;
696662306a36Sopenharmony_ci}
696762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_add_modes_noedid);
696862306a36Sopenharmony_ci
696962306a36Sopenharmony_ci/**
697062306a36Sopenharmony_ci * drm_set_preferred_mode - Sets the preferred mode of a connector
697162306a36Sopenharmony_ci * @connector: connector whose mode list should be processed
697262306a36Sopenharmony_ci * @hpref: horizontal resolution of preferred mode
697362306a36Sopenharmony_ci * @vpref: vertical resolution of preferred mode
697462306a36Sopenharmony_ci *
697562306a36Sopenharmony_ci * Marks a mode as preferred if it matches the resolution specified by @hpref
697662306a36Sopenharmony_ci * and @vpref.
697762306a36Sopenharmony_ci */
697862306a36Sopenharmony_civoid drm_set_preferred_mode(struct drm_connector *connector,
697962306a36Sopenharmony_ci			   int hpref, int vpref)
698062306a36Sopenharmony_ci{
698162306a36Sopenharmony_ci	struct drm_display_mode *mode;
698262306a36Sopenharmony_ci
698362306a36Sopenharmony_ci	list_for_each_entry(mode, &connector->probed_modes, head) {
698462306a36Sopenharmony_ci		if (mode->hdisplay == hpref &&
698562306a36Sopenharmony_ci		    mode->vdisplay == vpref)
698662306a36Sopenharmony_ci			mode->type |= DRM_MODE_TYPE_PREFERRED;
698762306a36Sopenharmony_ci	}
698862306a36Sopenharmony_ci}
698962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_set_preferred_mode);
699062306a36Sopenharmony_ci
699162306a36Sopenharmony_cistatic bool is_hdmi2_sink(const struct drm_connector *connector)
699262306a36Sopenharmony_ci{
699362306a36Sopenharmony_ci	/*
699462306a36Sopenharmony_ci	 * FIXME: sil-sii8620 doesn't have a connector around when
699562306a36Sopenharmony_ci	 * we need one, so we have to be prepared for a NULL connector.
699662306a36Sopenharmony_ci	 */
699762306a36Sopenharmony_ci	if (!connector)
699862306a36Sopenharmony_ci		return true;
699962306a36Sopenharmony_ci
700062306a36Sopenharmony_ci	return connector->display_info.hdmi.scdc.supported ||
700162306a36Sopenharmony_ci		connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR420;
700262306a36Sopenharmony_ci}
700362306a36Sopenharmony_ci
700462306a36Sopenharmony_cistatic u8 drm_mode_hdmi_vic(const struct drm_connector *connector,
700562306a36Sopenharmony_ci			    const struct drm_display_mode *mode)
700662306a36Sopenharmony_ci{
700762306a36Sopenharmony_ci	bool has_hdmi_infoframe = connector ?
700862306a36Sopenharmony_ci		connector->display_info.has_hdmi_infoframe : false;
700962306a36Sopenharmony_ci
701062306a36Sopenharmony_ci	if (!has_hdmi_infoframe)
701162306a36Sopenharmony_ci		return 0;
701262306a36Sopenharmony_ci
701362306a36Sopenharmony_ci	/* No HDMI VIC when signalling 3D video format */
701462306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_3D_MASK)
701562306a36Sopenharmony_ci		return 0;
701662306a36Sopenharmony_ci
701762306a36Sopenharmony_ci	return drm_match_hdmi_mode(mode);
701862306a36Sopenharmony_ci}
701962306a36Sopenharmony_ci
702062306a36Sopenharmony_cistatic u8 drm_mode_cea_vic(const struct drm_connector *connector,
702162306a36Sopenharmony_ci			   const struct drm_display_mode *mode)
702262306a36Sopenharmony_ci{
702362306a36Sopenharmony_ci	/*
702462306a36Sopenharmony_ci	 * HDMI spec says if a mode is found in HDMI 1.4b 4K modes
702562306a36Sopenharmony_ci	 * we should send its VIC in vendor infoframes, else send the
702662306a36Sopenharmony_ci	 * VIC in AVI infoframes. Lets check if this mode is present in
702762306a36Sopenharmony_ci	 * HDMI 1.4b 4K modes
702862306a36Sopenharmony_ci	 */
702962306a36Sopenharmony_ci	if (drm_mode_hdmi_vic(connector, mode))
703062306a36Sopenharmony_ci		return 0;
703162306a36Sopenharmony_ci
703262306a36Sopenharmony_ci	return drm_match_cea_mode(mode);
703362306a36Sopenharmony_ci}
703462306a36Sopenharmony_ci
703562306a36Sopenharmony_ci/*
703662306a36Sopenharmony_ci * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that
703762306a36Sopenharmony_ci * conform to HDMI 1.4.
703862306a36Sopenharmony_ci *
703962306a36Sopenharmony_ci * HDMI 1.4 (CTA-861-D) VIC range: [1..64]
704062306a36Sopenharmony_ci * HDMI 2.0 (CTA-861-F) VIC range: [1..107]
704162306a36Sopenharmony_ci *
704262306a36Sopenharmony_ci * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI
704362306a36Sopenharmony_ci * version.
704462306a36Sopenharmony_ci */
704562306a36Sopenharmony_cistatic u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic)
704662306a36Sopenharmony_ci{
704762306a36Sopenharmony_ci	if (!is_hdmi2_sink(connector) && vic > 64 &&
704862306a36Sopenharmony_ci	    !cta_vdb_has_vic(connector, vic))
704962306a36Sopenharmony_ci		return 0;
705062306a36Sopenharmony_ci
705162306a36Sopenharmony_ci	return vic;
705262306a36Sopenharmony_ci}
705362306a36Sopenharmony_ci
705462306a36Sopenharmony_ci/**
705562306a36Sopenharmony_ci * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
705662306a36Sopenharmony_ci *                                              data from a DRM display mode
705762306a36Sopenharmony_ci * @frame: HDMI AVI infoframe
705862306a36Sopenharmony_ci * @connector: the connector
705962306a36Sopenharmony_ci * @mode: DRM display mode
706062306a36Sopenharmony_ci *
706162306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure.
706262306a36Sopenharmony_ci */
706362306a36Sopenharmony_ciint
706462306a36Sopenharmony_cidrm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
706562306a36Sopenharmony_ci					 const struct drm_connector *connector,
706662306a36Sopenharmony_ci					 const struct drm_display_mode *mode)
706762306a36Sopenharmony_ci{
706862306a36Sopenharmony_ci	enum hdmi_picture_aspect picture_aspect;
706962306a36Sopenharmony_ci	u8 vic, hdmi_vic;
707062306a36Sopenharmony_ci
707162306a36Sopenharmony_ci	if (!frame || !mode)
707262306a36Sopenharmony_ci		return -EINVAL;
707362306a36Sopenharmony_ci
707462306a36Sopenharmony_ci	hdmi_avi_infoframe_init(frame);
707562306a36Sopenharmony_ci
707662306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
707762306a36Sopenharmony_ci		frame->pixel_repeat = 1;
707862306a36Sopenharmony_ci
707962306a36Sopenharmony_ci	vic = drm_mode_cea_vic(connector, mode);
708062306a36Sopenharmony_ci	hdmi_vic = drm_mode_hdmi_vic(connector, mode);
708162306a36Sopenharmony_ci
708262306a36Sopenharmony_ci	frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
708362306a36Sopenharmony_ci
708462306a36Sopenharmony_ci	/*
708562306a36Sopenharmony_ci	 * As some drivers don't support atomic, we can't use connector state.
708662306a36Sopenharmony_ci	 * So just initialize the frame with default values, just the same way
708762306a36Sopenharmony_ci	 * as it's done with other properties here.
708862306a36Sopenharmony_ci	 */
708962306a36Sopenharmony_ci	frame->content_type = HDMI_CONTENT_TYPE_GRAPHICS;
709062306a36Sopenharmony_ci	frame->itc = 0;
709162306a36Sopenharmony_ci
709262306a36Sopenharmony_ci	/*
709362306a36Sopenharmony_ci	 * Populate picture aspect ratio from either
709462306a36Sopenharmony_ci	 * user input (if specified) or from the CEA/HDMI mode lists.
709562306a36Sopenharmony_ci	 */
709662306a36Sopenharmony_ci	picture_aspect = mode->picture_aspect_ratio;
709762306a36Sopenharmony_ci	if (picture_aspect == HDMI_PICTURE_ASPECT_NONE) {
709862306a36Sopenharmony_ci		if (vic)
709962306a36Sopenharmony_ci			picture_aspect = drm_get_cea_aspect_ratio(vic);
710062306a36Sopenharmony_ci		else if (hdmi_vic)
710162306a36Sopenharmony_ci			picture_aspect = drm_get_hdmi_aspect_ratio(hdmi_vic);
710262306a36Sopenharmony_ci	}
710362306a36Sopenharmony_ci
710462306a36Sopenharmony_ci	/*
710562306a36Sopenharmony_ci	 * The infoframe can't convey anything but none, 4:3
710662306a36Sopenharmony_ci	 * and 16:9, so if the user has asked for anything else
710762306a36Sopenharmony_ci	 * we can only satisfy it by specifying the right VIC.
710862306a36Sopenharmony_ci	 */
710962306a36Sopenharmony_ci	if (picture_aspect > HDMI_PICTURE_ASPECT_16_9) {
711062306a36Sopenharmony_ci		if (vic) {
711162306a36Sopenharmony_ci			if (picture_aspect != drm_get_cea_aspect_ratio(vic))
711262306a36Sopenharmony_ci				return -EINVAL;
711362306a36Sopenharmony_ci		} else if (hdmi_vic) {
711462306a36Sopenharmony_ci			if (picture_aspect != drm_get_hdmi_aspect_ratio(hdmi_vic))
711562306a36Sopenharmony_ci				return -EINVAL;
711662306a36Sopenharmony_ci		} else {
711762306a36Sopenharmony_ci			return -EINVAL;
711862306a36Sopenharmony_ci		}
711962306a36Sopenharmony_ci
712062306a36Sopenharmony_ci		picture_aspect = HDMI_PICTURE_ASPECT_NONE;
712162306a36Sopenharmony_ci	}
712262306a36Sopenharmony_ci
712362306a36Sopenharmony_ci	frame->video_code = vic_for_avi_infoframe(connector, vic);
712462306a36Sopenharmony_ci	frame->picture_aspect = picture_aspect;
712562306a36Sopenharmony_ci	frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
712662306a36Sopenharmony_ci	frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
712762306a36Sopenharmony_ci
712862306a36Sopenharmony_ci	return 0;
712962306a36Sopenharmony_ci}
713062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
713162306a36Sopenharmony_ci
713262306a36Sopenharmony_ci/**
713362306a36Sopenharmony_ci * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
713462306a36Sopenharmony_ci *                                        quantization range information
713562306a36Sopenharmony_ci * @frame: HDMI AVI infoframe
713662306a36Sopenharmony_ci * @connector: the connector
713762306a36Sopenharmony_ci * @mode: DRM display mode
713862306a36Sopenharmony_ci * @rgb_quant_range: RGB quantization range (Q)
713962306a36Sopenharmony_ci */
714062306a36Sopenharmony_civoid
714162306a36Sopenharmony_cidrm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
714262306a36Sopenharmony_ci				   const struct drm_connector *connector,
714362306a36Sopenharmony_ci				   const struct drm_display_mode *mode,
714462306a36Sopenharmony_ci				   enum hdmi_quantization_range rgb_quant_range)
714562306a36Sopenharmony_ci{
714662306a36Sopenharmony_ci	const struct drm_display_info *info = &connector->display_info;
714762306a36Sopenharmony_ci
714862306a36Sopenharmony_ci	/*
714962306a36Sopenharmony_ci	 * CEA-861:
715062306a36Sopenharmony_ci	 * "A Source shall not send a non-zero Q value that does not correspond
715162306a36Sopenharmony_ci	 *  to the default RGB Quantization Range for the transmitted Picture
715262306a36Sopenharmony_ci	 *  unless the Sink indicates support for the Q bit in a Video
715362306a36Sopenharmony_ci	 *  Capabilities Data Block."
715462306a36Sopenharmony_ci	 *
715562306a36Sopenharmony_ci	 * HDMI 2.0 recommends sending non-zero Q when it does match the
715662306a36Sopenharmony_ci	 * default RGB quantization range for the mode, even when QS=0.
715762306a36Sopenharmony_ci	 */
715862306a36Sopenharmony_ci	if (info->rgb_quant_range_selectable ||
715962306a36Sopenharmony_ci	    rgb_quant_range == drm_default_rgb_quant_range(mode))
716062306a36Sopenharmony_ci		frame->quantization_range = rgb_quant_range;
716162306a36Sopenharmony_ci	else
716262306a36Sopenharmony_ci		frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
716362306a36Sopenharmony_ci
716462306a36Sopenharmony_ci	/*
716562306a36Sopenharmony_ci	 * CEA-861-F:
716662306a36Sopenharmony_ci	 * "When transmitting any RGB colorimetry, the Source should set the
716762306a36Sopenharmony_ci	 *  YQ-field to match the RGB Quantization Range being transmitted
716862306a36Sopenharmony_ci	 *  (e.g., when Limited Range RGB, set YQ=0 or when Full Range RGB,
716962306a36Sopenharmony_ci	 *  set YQ=1) and the Sink shall ignore the YQ-field."
717062306a36Sopenharmony_ci	 *
717162306a36Sopenharmony_ci	 * Unfortunate certain sinks (eg. VIZ Model 67/E261VA) get confused
717262306a36Sopenharmony_ci	 * by non-zero YQ when receiving RGB. There doesn't seem to be any
717362306a36Sopenharmony_ci	 * good way to tell which version of CEA-861 the sink supports, so
717462306a36Sopenharmony_ci	 * we limit non-zero YQ to HDMI 2.0 sinks only as HDMI 2.0 is based
717562306a36Sopenharmony_ci	 * on CEA-861-F.
717662306a36Sopenharmony_ci	 */
717762306a36Sopenharmony_ci	if (!is_hdmi2_sink(connector) ||
717862306a36Sopenharmony_ci	    rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED)
717962306a36Sopenharmony_ci		frame->ycc_quantization_range =
718062306a36Sopenharmony_ci			HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
718162306a36Sopenharmony_ci	else
718262306a36Sopenharmony_ci		frame->ycc_quantization_range =
718362306a36Sopenharmony_ci			HDMI_YCC_QUANTIZATION_RANGE_FULL;
718462306a36Sopenharmony_ci}
718562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_avi_infoframe_quant_range);
718662306a36Sopenharmony_ci
718762306a36Sopenharmony_cistatic enum hdmi_3d_structure
718862306a36Sopenharmony_cis3d_structure_from_display_mode(const struct drm_display_mode *mode)
718962306a36Sopenharmony_ci{
719062306a36Sopenharmony_ci	u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK;
719162306a36Sopenharmony_ci
719262306a36Sopenharmony_ci	switch (layout) {
719362306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_FRAME_PACKING:
719462306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_FRAME_PACKING;
719562306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
719662306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE;
719762306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
719862306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE;
719962306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
720062306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL;
720162306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_L_DEPTH:
720262306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_L_DEPTH;
720362306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
720462306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH;
720562306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
720662306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM;
720762306a36Sopenharmony_ci	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
720862306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF;
720962306a36Sopenharmony_ci	default:
721062306a36Sopenharmony_ci		return HDMI_3D_STRUCTURE_INVALID;
721162306a36Sopenharmony_ci	}
721262306a36Sopenharmony_ci}
721362306a36Sopenharmony_ci
721462306a36Sopenharmony_ci/**
721562306a36Sopenharmony_ci * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
721662306a36Sopenharmony_ci * data from a DRM display mode
721762306a36Sopenharmony_ci * @frame: HDMI vendor infoframe
721862306a36Sopenharmony_ci * @connector: the connector
721962306a36Sopenharmony_ci * @mode: DRM display mode
722062306a36Sopenharmony_ci *
722162306a36Sopenharmony_ci * Note that there's is a need to send HDMI vendor infoframes only when using a
722262306a36Sopenharmony_ci * 4k or stereoscopic 3D mode. So when giving any other mode as input this
722362306a36Sopenharmony_ci * function will return -EINVAL, error that can be safely ignored.
722462306a36Sopenharmony_ci *
722562306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure.
722662306a36Sopenharmony_ci */
722762306a36Sopenharmony_ciint
722862306a36Sopenharmony_cidrm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
722962306a36Sopenharmony_ci					    const struct drm_connector *connector,
723062306a36Sopenharmony_ci					    const struct drm_display_mode *mode)
723162306a36Sopenharmony_ci{
723262306a36Sopenharmony_ci	/*
723362306a36Sopenharmony_ci	 * FIXME: sil-sii8620 doesn't have a connector around when
723462306a36Sopenharmony_ci	 * we need one, so we have to be prepared for a NULL connector.
723562306a36Sopenharmony_ci	 */
723662306a36Sopenharmony_ci	bool has_hdmi_infoframe = connector ?
723762306a36Sopenharmony_ci		connector->display_info.has_hdmi_infoframe : false;
723862306a36Sopenharmony_ci	int err;
723962306a36Sopenharmony_ci
724062306a36Sopenharmony_ci	if (!frame || !mode)
724162306a36Sopenharmony_ci		return -EINVAL;
724262306a36Sopenharmony_ci
724362306a36Sopenharmony_ci	if (!has_hdmi_infoframe)
724462306a36Sopenharmony_ci		return -EINVAL;
724562306a36Sopenharmony_ci
724662306a36Sopenharmony_ci	err = hdmi_vendor_infoframe_init(frame);
724762306a36Sopenharmony_ci	if (err < 0)
724862306a36Sopenharmony_ci		return err;
724962306a36Sopenharmony_ci
725062306a36Sopenharmony_ci	/*
725162306a36Sopenharmony_ci	 * Even if it's not absolutely necessary to send the infoframe
725262306a36Sopenharmony_ci	 * (ie.vic==0 and s3d_struct==0) we will still send it if we
725362306a36Sopenharmony_ci	 * know that the sink can handle it. This is based on a
725462306a36Sopenharmony_ci	 * suggestion in HDMI 2.0 Appendix F. Apparently some sinks
725562306a36Sopenharmony_ci	 * have trouble realizing that they should switch from 3D to 2D
725662306a36Sopenharmony_ci	 * mode if the source simply stops sending the infoframe when
725762306a36Sopenharmony_ci	 * it wants to switch from 3D to 2D.
725862306a36Sopenharmony_ci	 */
725962306a36Sopenharmony_ci	frame->vic = drm_mode_hdmi_vic(connector, mode);
726062306a36Sopenharmony_ci	frame->s3d_struct = s3d_structure_from_display_mode(mode);
726162306a36Sopenharmony_ci
726262306a36Sopenharmony_ci	return 0;
726362306a36Sopenharmony_ci}
726462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
726562306a36Sopenharmony_ci
726662306a36Sopenharmony_cistatic void drm_parse_tiled_block(struct drm_connector *connector,
726762306a36Sopenharmony_ci				  const struct displayid_block *block)
726862306a36Sopenharmony_ci{
726962306a36Sopenharmony_ci	const struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;
727062306a36Sopenharmony_ci	u16 w, h;
727162306a36Sopenharmony_ci	u8 tile_v_loc, tile_h_loc;
727262306a36Sopenharmony_ci	u8 num_v_tile, num_h_tile;
727362306a36Sopenharmony_ci	struct drm_tile_group *tg;
727462306a36Sopenharmony_ci
727562306a36Sopenharmony_ci	w = tile->tile_size[0] | tile->tile_size[1] << 8;
727662306a36Sopenharmony_ci	h = tile->tile_size[2] | tile->tile_size[3] << 8;
727762306a36Sopenharmony_ci
727862306a36Sopenharmony_ci	num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
727962306a36Sopenharmony_ci	num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
728062306a36Sopenharmony_ci	tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4);
728162306a36Sopenharmony_ci	tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4);
728262306a36Sopenharmony_ci
728362306a36Sopenharmony_ci	connector->has_tile = true;
728462306a36Sopenharmony_ci	if (tile->tile_cap & 0x80)
728562306a36Sopenharmony_ci		connector->tile_is_single_monitor = true;
728662306a36Sopenharmony_ci
728762306a36Sopenharmony_ci	connector->num_h_tile = num_h_tile + 1;
728862306a36Sopenharmony_ci	connector->num_v_tile = num_v_tile + 1;
728962306a36Sopenharmony_ci	connector->tile_h_loc = tile_h_loc;
729062306a36Sopenharmony_ci	connector->tile_v_loc = tile_v_loc;
729162306a36Sopenharmony_ci	connector->tile_h_size = w + 1;
729262306a36Sopenharmony_ci	connector->tile_v_size = h + 1;
729362306a36Sopenharmony_ci
729462306a36Sopenharmony_ci	drm_dbg_kms(connector->dev,
729562306a36Sopenharmony_ci		    "[CONNECTOR:%d:%s] tile cap 0x%x, size %dx%d, num tiles %dx%d, location %dx%d, vend %c%c%c",
729662306a36Sopenharmony_ci		    connector->base.id, connector->name,
729762306a36Sopenharmony_ci		    tile->tile_cap,
729862306a36Sopenharmony_ci		    connector->tile_h_size, connector->tile_v_size,
729962306a36Sopenharmony_ci		    connector->num_h_tile, connector->num_v_tile,
730062306a36Sopenharmony_ci		    connector->tile_h_loc, connector->tile_v_loc,
730162306a36Sopenharmony_ci		    tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);
730262306a36Sopenharmony_ci
730362306a36Sopenharmony_ci	tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
730462306a36Sopenharmony_ci	if (!tg)
730562306a36Sopenharmony_ci		tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
730662306a36Sopenharmony_ci	if (!tg)
730762306a36Sopenharmony_ci		return;
730862306a36Sopenharmony_ci
730962306a36Sopenharmony_ci	if (connector->tile_group != tg) {
731062306a36Sopenharmony_ci		/* if we haven't got a pointer,
731162306a36Sopenharmony_ci		   take the reference, drop ref to old tile group */
731262306a36Sopenharmony_ci		if (connector->tile_group)
731362306a36Sopenharmony_ci			drm_mode_put_tile_group(connector->dev, connector->tile_group);
731462306a36Sopenharmony_ci		connector->tile_group = tg;
731562306a36Sopenharmony_ci	} else {
731662306a36Sopenharmony_ci		/* if same tile group, then release the ref we just took. */
731762306a36Sopenharmony_ci		drm_mode_put_tile_group(connector->dev, tg);
731862306a36Sopenharmony_ci	}
731962306a36Sopenharmony_ci}
732062306a36Sopenharmony_ci
732162306a36Sopenharmony_cistatic bool displayid_is_tiled_block(const struct displayid_iter *iter,
732262306a36Sopenharmony_ci				     const struct displayid_block *block)
732362306a36Sopenharmony_ci{
732462306a36Sopenharmony_ci	return (displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_12 &&
732562306a36Sopenharmony_ci		block->tag == DATA_BLOCK_TILED_DISPLAY) ||
732662306a36Sopenharmony_ci		(displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 &&
732762306a36Sopenharmony_ci		 block->tag == DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY);
732862306a36Sopenharmony_ci}
732962306a36Sopenharmony_ci
733062306a36Sopenharmony_cistatic void _drm_update_tile_info(struct drm_connector *connector,
733162306a36Sopenharmony_ci				  const struct drm_edid *drm_edid)
733262306a36Sopenharmony_ci{
733362306a36Sopenharmony_ci	const struct displayid_block *block;
733462306a36Sopenharmony_ci	struct displayid_iter iter;
733562306a36Sopenharmony_ci
733662306a36Sopenharmony_ci	connector->has_tile = false;
733762306a36Sopenharmony_ci
733862306a36Sopenharmony_ci	displayid_iter_edid_begin(drm_edid, &iter);
733962306a36Sopenharmony_ci	displayid_iter_for_each(block, &iter) {
734062306a36Sopenharmony_ci		if (displayid_is_tiled_block(&iter, block))
734162306a36Sopenharmony_ci			drm_parse_tiled_block(connector, block);
734262306a36Sopenharmony_ci	}
734362306a36Sopenharmony_ci	displayid_iter_end(&iter);
734462306a36Sopenharmony_ci
734562306a36Sopenharmony_ci	if (!connector->has_tile && connector->tile_group) {
734662306a36Sopenharmony_ci		drm_mode_put_tile_group(connector->dev, connector->tile_group);
734762306a36Sopenharmony_ci		connector->tile_group = NULL;
734862306a36Sopenharmony_ci	}
734962306a36Sopenharmony_ci}
7350