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