1746fbbdbSjsg /* 2746fbbdbSjsg * Copyright © 1997-2003 by The XFree86 Project, Inc. 3746fbbdbSjsg * Copyright © 2007 Dave Airlie 4746fbbdbSjsg * Copyright © 2007-2008 Intel Corporation 5746fbbdbSjsg * Jesse Barnes <jesse.barnes@intel.com> 6746fbbdbSjsg * Copyright 2005-2006 Luc Verhaegen 7746fbbdbSjsg * Copyright (c) 2001, Andy Ritger aritger@nvidia.com 8746fbbdbSjsg * 9746fbbdbSjsg * Permission is hereby granted, free of charge, to any person obtaining a 10746fbbdbSjsg * copy of this software and associated documentation files (the "Software"), 11746fbbdbSjsg * to deal in the Software without restriction, including without limitation 12746fbbdbSjsg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13746fbbdbSjsg * and/or sell copies of the Software, and to permit persons to whom the 14746fbbdbSjsg * Software is furnished to do so, subject to the following conditions: 15746fbbdbSjsg * 16746fbbdbSjsg * The above copyright notice and this permission notice shall be included in 17746fbbdbSjsg * all copies or substantial portions of the Software. 18746fbbdbSjsg * 19746fbbdbSjsg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20746fbbdbSjsg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21746fbbdbSjsg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22746fbbdbSjsg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 23746fbbdbSjsg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24746fbbdbSjsg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25746fbbdbSjsg * OTHER DEALINGS IN THE SOFTWARE. 26746fbbdbSjsg * 27746fbbdbSjsg * Except as contained in this notice, the name of the copyright holder(s) 28746fbbdbSjsg * and author(s) shall not be used in advertising or otherwise to promote 29746fbbdbSjsg * the sale, use or other dealings in this Software without prior written 30746fbbdbSjsg * authorization from the copyright holder(s) and author(s). 31746fbbdbSjsg */ 32746fbbdbSjsg 33c349dbc7Sjsg #include <linux/ctype.h> 34f005ef32Sjsg #include <linux/export.h> 35f005ef32Sjsg #include <linux/fb.h> /* for KHZ2PICOS() */ 363253c27bSkettenis #include <linux/list.h> 373253c27bSkettenis #include <linux/list_sort.h> 38f005ef32Sjsg #include <linux/of.h> 39c349dbc7Sjsg 401bb76ff1Sjsg #include <video/of_display_timing.h> 413253c27bSkettenis #include <video/of_videomode.h> 423253c27bSkettenis #include <video/videomode.h> 43c349dbc7Sjsg 44c349dbc7Sjsg #include <drm/drm_crtc.h> 45c349dbc7Sjsg #include <drm/drm_device.h> 461bb76ff1Sjsg #include <drm/drm_edid.h> 477f4dd379Sjsg #include <drm/drm_modes.h> 48c349dbc7Sjsg #include <drm/drm_print.h> 49746fbbdbSjsg 503253c27bSkettenis #include "drm_crtc_internal.h" 51746fbbdbSjsg 52746fbbdbSjsg /** 533253c27bSkettenis * drm_mode_debug_printmodeline - print a mode to dmesg 54746fbbdbSjsg * @mode: mode to print 55746fbbdbSjsg * 56746fbbdbSjsg * Describe @mode using DRM_DEBUG. 57746fbbdbSjsg */ 58d7873f4eSjsg void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) 59746fbbdbSjsg { 607f4dd379Sjsg DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); 61746fbbdbSjsg } 62746fbbdbSjsg EXPORT_SYMBOL(drm_mode_debug_printmodeline); 63746fbbdbSjsg 64746fbbdbSjsg /** 653253c27bSkettenis * drm_mode_create - create a new display mode 66746fbbdbSjsg * @dev: DRM device 673253c27bSkettenis * 683253c27bSkettenis * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it 693253c27bSkettenis * and return it. 703253c27bSkettenis * 713253c27bSkettenis * Returns: 723253c27bSkettenis * Pointer to new mode on success, NULL on error. 733253c27bSkettenis */ 743253c27bSkettenis struct drm_display_mode *drm_mode_create(struct drm_device *dev) 753253c27bSkettenis { 763253c27bSkettenis struct drm_display_mode *nmode; 773253c27bSkettenis 783253c27bSkettenis nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); 793253c27bSkettenis if (!nmode) 803253c27bSkettenis return NULL; 813253c27bSkettenis 823253c27bSkettenis return nmode; 833253c27bSkettenis } 843253c27bSkettenis EXPORT_SYMBOL(drm_mode_create); 853253c27bSkettenis 863253c27bSkettenis /** 873253c27bSkettenis * drm_mode_destroy - remove a mode 883253c27bSkettenis * @dev: DRM device 893253c27bSkettenis * @mode: mode to remove 903253c27bSkettenis * 913253c27bSkettenis * Release @mode's unique ID, then free it @mode structure itself using kfree. 923253c27bSkettenis */ 933253c27bSkettenis void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) 943253c27bSkettenis { 953253c27bSkettenis if (!mode) 963253c27bSkettenis return; 973253c27bSkettenis 983253c27bSkettenis kfree(mode); 993253c27bSkettenis } 1003253c27bSkettenis EXPORT_SYMBOL(drm_mode_destroy); 1013253c27bSkettenis 1023253c27bSkettenis /** 1033253c27bSkettenis * drm_mode_probed_add - add a mode to a connector's probed_mode list 1043253c27bSkettenis * @connector: connector the new mode 1053253c27bSkettenis * @mode: mode data 1063253c27bSkettenis * 1073253c27bSkettenis * Add @mode to @connector's probed_mode list for later use. This list should 1083253c27bSkettenis * then in a second step get filtered and all the modes actually supported by 1093253c27bSkettenis * the hardware moved to the @connector's modes list. 1103253c27bSkettenis */ 1113253c27bSkettenis void drm_mode_probed_add(struct drm_connector *connector, 1123253c27bSkettenis struct drm_display_mode *mode) 1133253c27bSkettenis { 1143253c27bSkettenis WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); 1153253c27bSkettenis 1163253c27bSkettenis list_add_tail(&mode->head, &connector->probed_modes); 1173253c27bSkettenis } 1183253c27bSkettenis EXPORT_SYMBOL(drm_mode_probed_add); 1193253c27bSkettenis 120f005ef32Sjsg enum drm_mode_analog { 121f005ef32Sjsg DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */ 122f005ef32Sjsg DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */ 123f005ef32Sjsg }; 124f005ef32Sjsg 125f005ef32Sjsg /* 126f005ef32Sjsg * The timings come from: 127f005ef32Sjsg * - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html 128f005ef32Sjsg * - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html 129f005ef32Sjsg * - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm 130f005ef32Sjsg */ 131f005ef32Sjsg #define NTSC_LINE_DURATION_NS 63556U 132f005ef32Sjsg #define NTSC_LINES_NUMBER 525 133f005ef32Sjsg 134f005ef32Sjsg #define NTSC_HBLK_DURATION_TYP_NS 10900U 135f005ef32Sjsg #define NTSC_HBLK_DURATION_MIN_NS (NTSC_HBLK_DURATION_TYP_NS - 200) 136f005ef32Sjsg #define NTSC_HBLK_DURATION_MAX_NS (NTSC_HBLK_DURATION_TYP_NS + 200) 137f005ef32Sjsg 138f005ef32Sjsg #define NTSC_HACT_DURATION_TYP_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS) 139f005ef32Sjsg #define NTSC_HACT_DURATION_MIN_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS) 140f005ef32Sjsg #define NTSC_HACT_DURATION_MAX_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS) 141f005ef32Sjsg 142f005ef32Sjsg #define NTSC_HFP_DURATION_TYP_NS 1500 143f005ef32Sjsg #define NTSC_HFP_DURATION_MIN_NS 1270 144f005ef32Sjsg #define NTSC_HFP_DURATION_MAX_NS 2220 145f005ef32Sjsg 146f005ef32Sjsg #define NTSC_HSLEN_DURATION_TYP_NS 4700 147f005ef32Sjsg #define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100) 148f005ef32Sjsg #define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100) 149f005ef32Sjsg 150f005ef32Sjsg #define NTSC_HBP_DURATION_TYP_NS 4700 151f005ef32Sjsg 152f005ef32Sjsg /* 153f005ef32Sjsg * I couldn't find the actual tolerance for the back porch, so let's 154f005ef32Sjsg * just reuse the sync length ones. 155f005ef32Sjsg */ 156f005ef32Sjsg #define NTSC_HBP_DURATION_MIN_NS (NTSC_HBP_DURATION_TYP_NS - 100) 157f005ef32Sjsg #define NTSC_HBP_DURATION_MAX_NS (NTSC_HBP_DURATION_TYP_NS + 100) 158f005ef32Sjsg 159f005ef32Sjsg #define PAL_LINE_DURATION_NS 64000U 160f005ef32Sjsg #define PAL_LINES_NUMBER 625 161f005ef32Sjsg 162f005ef32Sjsg #define PAL_HACT_DURATION_TYP_NS 51950U 163f005ef32Sjsg #define PAL_HACT_DURATION_MIN_NS (PAL_HACT_DURATION_TYP_NS - 100) 164f005ef32Sjsg #define PAL_HACT_DURATION_MAX_NS (PAL_HACT_DURATION_TYP_NS + 400) 165f005ef32Sjsg 166f005ef32Sjsg #define PAL_HBLK_DURATION_TYP_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS) 167f005ef32Sjsg #define PAL_HBLK_DURATION_MIN_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS) 168f005ef32Sjsg #define PAL_HBLK_DURATION_MAX_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS) 169f005ef32Sjsg 170f005ef32Sjsg #define PAL_HFP_DURATION_TYP_NS 1650 171f005ef32Sjsg #define PAL_HFP_DURATION_MIN_NS (PAL_HFP_DURATION_TYP_NS - 100) 172f005ef32Sjsg #define PAL_HFP_DURATION_MAX_NS (PAL_HFP_DURATION_TYP_NS + 400) 173f005ef32Sjsg 174f005ef32Sjsg #define PAL_HSLEN_DURATION_TYP_NS 4700 175f005ef32Sjsg #define PAL_HSLEN_DURATION_MIN_NS (PAL_HSLEN_DURATION_TYP_NS - 200) 176f005ef32Sjsg #define PAL_HSLEN_DURATION_MAX_NS (PAL_HSLEN_DURATION_TYP_NS + 200) 177f005ef32Sjsg 178f005ef32Sjsg #define PAL_HBP_DURATION_TYP_NS 5700 179f005ef32Sjsg #define PAL_HBP_DURATION_MIN_NS (PAL_HBP_DURATION_TYP_NS - 200) 180f005ef32Sjsg #define PAL_HBP_DURATION_MAX_NS (PAL_HBP_DURATION_TYP_NS + 200) 181f005ef32Sjsg 182f005ef32Sjsg struct analog_param_field { 183f005ef32Sjsg unsigned int even, odd; 184f005ef32Sjsg }; 185f005ef32Sjsg 186f005ef32Sjsg #define PARAM_FIELD(_odd, _even) \ 187f005ef32Sjsg { .even = _even, .odd = _odd } 188f005ef32Sjsg 189f005ef32Sjsg struct analog_param_range { 190f005ef32Sjsg unsigned int min, typ, max; 191f005ef32Sjsg }; 192f005ef32Sjsg 193f005ef32Sjsg #define PARAM_RANGE(_min, _typ, _max) \ 194f005ef32Sjsg { .min = _min, .typ = _typ, .max = _max } 195f005ef32Sjsg 196f005ef32Sjsg struct analog_parameters { 197f005ef32Sjsg unsigned int num_lines; 198f005ef32Sjsg unsigned int line_duration_ns; 199f005ef32Sjsg 200f005ef32Sjsg struct analog_param_range hact_ns; 201f005ef32Sjsg struct analog_param_range hfp_ns; 202f005ef32Sjsg struct analog_param_range hslen_ns; 203f005ef32Sjsg struct analog_param_range hbp_ns; 204f005ef32Sjsg struct analog_param_range hblk_ns; 205f005ef32Sjsg 206f005ef32Sjsg unsigned int bt601_hfp; 207f005ef32Sjsg 208f005ef32Sjsg struct analog_param_field vfp_lines; 209f005ef32Sjsg struct analog_param_field vslen_lines; 210f005ef32Sjsg struct analog_param_field vbp_lines; 211f005ef32Sjsg }; 212f005ef32Sjsg 213f005ef32Sjsg #define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp, \ 214f005ef32Sjsg _hslen, _hbp, _hblk, _bt601_hfp, _vfp, \ 215f005ef32Sjsg _vslen, _vbp) \ 216f005ef32Sjsg [_mode] = { \ 217f005ef32Sjsg .num_lines = _lines, \ 218f005ef32Sjsg .line_duration_ns = _line_dur, \ 219f005ef32Sjsg .hact_ns = _hact, \ 220f005ef32Sjsg .hfp_ns = _hfp, \ 221f005ef32Sjsg .hslen_ns = _hslen, \ 222f005ef32Sjsg .hbp_ns = _hbp, \ 223f005ef32Sjsg .hblk_ns = _hblk, \ 224f005ef32Sjsg .bt601_hfp = _bt601_hfp, \ 225f005ef32Sjsg .vfp_lines = _vfp, \ 226f005ef32Sjsg .vslen_lines = _vslen, \ 227f005ef32Sjsg .vbp_lines = _vbp, \ 228f005ef32Sjsg } 229f005ef32Sjsg 230f005ef32Sjsg static const struct analog_parameters tv_modes_parameters[] = { 231f005ef32Sjsg TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC, 232f005ef32Sjsg NTSC_LINES_NUMBER, 233f005ef32Sjsg NTSC_LINE_DURATION_NS, 234f005ef32Sjsg PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS, 235f005ef32Sjsg NTSC_HACT_DURATION_TYP_NS, 236f005ef32Sjsg NTSC_HACT_DURATION_MAX_NS), 237f005ef32Sjsg PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS, 238f005ef32Sjsg NTSC_HFP_DURATION_TYP_NS, 239f005ef32Sjsg NTSC_HFP_DURATION_MAX_NS), 240f005ef32Sjsg PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS, 241f005ef32Sjsg NTSC_HSLEN_DURATION_TYP_NS, 242f005ef32Sjsg NTSC_HSLEN_DURATION_MAX_NS), 243f005ef32Sjsg PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS, 244f005ef32Sjsg NTSC_HBP_DURATION_TYP_NS, 245f005ef32Sjsg NTSC_HBP_DURATION_MAX_NS), 246f005ef32Sjsg PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS, 247f005ef32Sjsg NTSC_HBLK_DURATION_TYP_NS, 248f005ef32Sjsg NTSC_HBLK_DURATION_MAX_NS), 249f005ef32Sjsg 16, 250f005ef32Sjsg PARAM_FIELD(3, 3), 251f005ef32Sjsg PARAM_FIELD(3, 3), 252f005ef32Sjsg PARAM_FIELD(16, 17)), 253f005ef32Sjsg TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL, 254f005ef32Sjsg PAL_LINES_NUMBER, 255f005ef32Sjsg PAL_LINE_DURATION_NS, 256f005ef32Sjsg PARAM_RANGE(PAL_HACT_DURATION_MIN_NS, 257f005ef32Sjsg PAL_HACT_DURATION_TYP_NS, 258f005ef32Sjsg PAL_HACT_DURATION_MAX_NS), 259f005ef32Sjsg PARAM_RANGE(PAL_HFP_DURATION_MIN_NS, 260f005ef32Sjsg PAL_HFP_DURATION_TYP_NS, 261f005ef32Sjsg PAL_HFP_DURATION_MAX_NS), 262f005ef32Sjsg PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS, 263f005ef32Sjsg PAL_HSLEN_DURATION_TYP_NS, 264f005ef32Sjsg PAL_HSLEN_DURATION_MAX_NS), 265f005ef32Sjsg PARAM_RANGE(PAL_HBP_DURATION_MIN_NS, 266f005ef32Sjsg PAL_HBP_DURATION_TYP_NS, 267f005ef32Sjsg PAL_HBP_DURATION_MAX_NS), 268f005ef32Sjsg PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS, 269f005ef32Sjsg PAL_HBLK_DURATION_TYP_NS, 270f005ef32Sjsg PAL_HBLK_DURATION_MAX_NS), 271f005ef32Sjsg 12, 272f005ef32Sjsg 273f005ef32Sjsg /* 274f005ef32Sjsg * The front porch is actually 6 short sync 275f005ef32Sjsg * pulses for the even field, and 5 for the 276f005ef32Sjsg * odd field. Each sync takes half a life so 277f005ef32Sjsg * the odd field front porch is shorter by 278f005ef32Sjsg * half a line. 279f005ef32Sjsg * 280f005ef32Sjsg * In progressive, we're supposed to use 6 281f005ef32Sjsg * pulses, so we're fine there 282f005ef32Sjsg */ 283f005ef32Sjsg PARAM_FIELD(3, 2), 284f005ef32Sjsg 285f005ef32Sjsg /* 286f005ef32Sjsg * The vsync length is 5 long sync pulses, 287f005ef32Sjsg * each field taking half a line. We're 288f005ef32Sjsg * shorter for both fields by half a line. 289f005ef32Sjsg * 290f005ef32Sjsg * In progressive, we're supposed to use 5 291f005ef32Sjsg * pulses, so we're off by half 292f005ef32Sjsg * a line. 293f005ef32Sjsg * 294f005ef32Sjsg * In interlace, we're now off by half a line 295f005ef32Sjsg * for the even field and one line for the odd 296f005ef32Sjsg * field. 297f005ef32Sjsg */ 298f005ef32Sjsg PARAM_FIELD(3, 3), 299f005ef32Sjsg 300f005ef32Sjsg /* 301f005ef32Sjsg * The back porch starts with post-equalizing 302f005ef32Sjsg * pulses, consisting in 5 short sync pulses 303f005ef32Sjsg * for the even field, 4 for the odd field. In 304f005ef32Sjsg * progressive, it's 5 short syncs. 305f005ef32Sjsg * 306f005ef32Sjsg * In progressive, we thus have 2.5 lines, 307f005ef32Sjsg * plus the 0.5 line we were missing 308f005ef32Sjsg * previously, so we should use 3 lines. 309f005ef32Sjsg * 310f005ef32Sjsg * In interlace, the even field is in the 311f005ef32Sjsg * exact same case than progressive. For the 312f005ef32Sjsg * odd field, we should be using 2 lines but 313f005ef32Sjsg * we're one line short, so we'll make up for 314f005ef32Sjsg * it here by using 3. 315f005ef32Sjsg * 316f005ef32Sjsg * The entire blanking area is supposed to 317f005ef32Sjsg * take 25 lines, so we also need to account 318f005ef32Sjsg * for the rest of the blanking area that 319f005ef32Sjsg * can't be in either the front porch or sync 320f005ef32Sjsg * period. 321f005ef32Sjsg */ 322f005ef32Sjsg PARAM_FIELD(19, 20)), 323f005ef32Sjsg }; 324f005ef32Sjsg 325f005ef32Sjsg static int fill_analog_mode(struct drm_device *dev, 326f005ef32Sjsg struct drm_display_mode *mode, 327f005ef32Sjsg const struct analog_parameters *params, 328f005ef32Sjsg unsigned long pixel_clock_hz, 329f005ef32Sjsg unsigned int hactive, 330f005ef32Sjsg unsigned int vactive, 331f005ef32Sjsg bool interlace) 332f005ef32Sjsg { 333f005ef32Sjsg unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz; 334f005ef32Sjsg unsigned int htotal, vtotal; 335f005ef32Sjsg unsigned int max_hact, hact_duration_ns; 336f005ef32Sjsg unsigned int hblk, hblk_duration_ns; 337f005ef32Sjsg unsigned int hfp, hfp_duration_ns; 338f005ef32Sjsg unsigned int hslen, hslen_duration_ns; 339f005ef32Sjsg unsigned int hbp, hbp_duration_ns; 340f005ef32Sjsg unsigned int porches, porches_duration_ns; 341f005ef32Sjsg unsigned int vfp, vfp_min; 342f005ef32Sjsg unsigned int vbp, vbp_min; 343f005ef32Sjsg unsigned int vslen; 344f005ef32Sjsg bool bt601 = false; 345f005ef32Sjsg int porches_rem; 346f005ef32Sjsg u64 result; 347f005ef32Sjsg 348f005ef32Sjsg drm_dbg_kms(dev, 349f005ef32Sjsg "Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n", 350f005ef32Sjsg hactive, vactive, 351f005ef32Sjsg interlace ? 'i' : 'p', 352f005ef32Sjsg params->num_lines, 353f005ef32Sjsg pixel_clock_hz / 1000); 354f005ef32Sjsg 355f005ef32Sjsg max_hact = params->hact_ns.max / pixel_duration_ns; 356f005ef32Sjsg if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) { 357f005ef32Sjsg drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n"); 358f005ef32Sjsg bt601 = true; 359f005ef32Sjsg } 360f005ef32Sjsg 361f005ef32Sjsg /* 362f005ef32Sjsg * Our pixel duration is going to be round down by the division, 363f005ef32Sjsg * so rounding up is probably going to introduce even more 364f005ef32Sjsg * deviation. 365f005ef32Sjsg */ 366f005ef32Sjsg result = (u64)params->line_duration_ns * pixel_clock_hz; 367f005ef32Sjsg do_div(result, NSEC_PER_SEC); 368f005ef32Sjsg htotal = result; 369f005ef32Sjsg 370f005ef32Sjsg drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal); 371f005ef32Sjsg 372f005ef32Sjsg hact_duration_ns = hactive * pixel_duration_ns; 373f005ef32Sjsg if (!bt601 && 374f005ef32Sjsg (hact_duration_ns < params->hact_ns.min || 375f005ef32Sjsg hact_duration_ns > params->hact_ns.max)) { 376f005ef32Sjsg DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n", 377f005ef32Sjsg hact_duration_ns, params->hact_ns.min, params->hact_ns.max); 378f005ef32Sjsg return -EINVAL; 379f005ef32Sjsg } 380f005ef32Sjsg 381f005ef32Sjsg hblk = htotal - hactive; 382f005ef32Sjsg drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk); 383f005ef32Sjsg 384f005ef32Sjsg hblk_duration_ns = hblk * pixel_duration_ns; 385f005ef32Sjsg if (!bt601 && 386f005ef32Sjsg (hblk_duration_ns < params->hblk_ns.min || 387f005ef32Sjsg hblk_duration_ns > params->hblk_ns.max)) { 388f005ef32Sjsg DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n", 389f005ef32Sjsg hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max); 390f005ef32Sjsg return -EINVAL; 391f005ef32Sjsg } 392f005ef32Sjsg 393f005ef32Sjsg hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns); 394f005ef32Sjsg drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen); 395f005ef32Sjsg 396f005ef32Sjsg hslen_duration_ns = hslen * pixel_duration_ns; 397f005ef32Sjsg if (!bt601 && 398f005ef32Sjsg (hslen_duration_ns < params->hslen_ns.min || 399f005ef32Sjsg hslen_duration_ns > params->hslen_ns.max)) { 400f005ef32Sjsg DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n", 401f005ef32Sjsg hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max); 402f005ef32Sjsg return -EINVAL; 403f005ef32Sjsg } 404f005ef32Sjsg 405f005ef32Sjsg porches = hblk - hslen; 406f005ef32Sjsg drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches); 407f005ef32Sjsg 408f005ef32Sjsg porches_duration_ns = porches * pixel_duration_ns; 409f005ef32Sjsg if (!bt601 && 410f005ef32Sjsg (porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) || 411f005ef32Sjsg porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) { 412f005ef32Sjsg DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns); 413f005ef32Sjsg return -EINVAL; 414f005ef32Sjsg } 415f005ef32Sjsg 416f005ef32Sjsg if (bt601) { 417f005ef32Sjsg hfp = params->bt601_hfp; 418f005ef32Sjsg } else { 419f005ef32Sjsg unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min, 420f005ef32Sjsg pixel_duration_ns); 421f005ef32Sjsg unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min, 422f005ef32Sjsg pixel_duration_ns); 423f005ef32Sjsg int porches_rem = porches - hfp_min - hbp_min; 424f005ef32Sjsg 425f005ef32Sjsg hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2); 426f005ef32Sjsg } 427f005ef32Sjsg 428f005ef32Sjsg drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp); 429f005ef32Sjsg 430f005ef32Sjsg hfp_duration_ns = hfp * pixel_duration_ns; 431f005ef32Sjsg if (!bt601 && 432f005ef32Sjsg (hfp_duration_ns < params->hfp_ns.min || 433f005ef32Sjsg hfp_duration_ns > params->hfp_ns.max)) { 434f005ef32Sjsg DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n", 435f005ef32Sjsg hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max); 436f005ef32Sjsg return -EINVAL; 437f005ef32Sjsg } 438f005ef32Sjsg 439f005ef32Sjsg hbp = porches - hfp; 440f005ef32Sjsg drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp); 441f005ef32Sjsg 442f005ef32Sjsg hbp_duration_ns = hbp * pixel_duration_ns; 443f005ef32Sjsg if (!bt601 && 444f005ef32Sjsg (hbp_duration_ns < params->hbp_ns.min || 445f005ef32Sjsg hbp_duration_ns > params->hbp_ns.max)) { 446f005ef32Sjsg DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n", 447f005ef32Sjsg hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max); 448f005ef32Sjsg return -EINVAL; 449f005ef32Sjsg } 450f005ef32Sjsg 451f005ef32Sjsg if (htotal != (hactive + hfp + hslen + hbp)) 452f005ef32Sjsg return -EINVAL; 453f005ef32Sjsg 454f005ef32Sjsg mode->clock = pixel_clock_hz / 1000; 455f005ef32Sjsg mode->hdisplay = hactive; 456f005ef32Sjsg mode->hsync_start = mode->hdisplay + hfp; 457f005ef32Sjsg mode->hsync_end = mode->hsync_start + hslen; 458f005ef32Sjsg mode->htotal = mode->hsync_end + hbp; 459f005ef32Sjsg 460f005ef32Sjsg if (interlace) { 461f005ef32Sjsg vfp_min = params->vfp_lines.even + params->vfp_lines.odd; 462f005ef32Sjsg vbp_min = params->vbp_lines.even + params->vbp_lines.odd; 463f005ef32Sjsg vslen = params->vslen_lines.even + params->vslen_lines.odd; 464f005ef32Sjsg } else { 465f005ef32Sjsg /* 466f005ef32Sjsg * By convention, NTSC (aka 525/60) systems start with 467f005ef32Sjsg * the even field, but PAL (aka 625/50) systems start 468f005ef32Sjsg * with the odd one. 469f005ef32Sjsg * 470f005ef32Sjsg * PAL systems also have asymmetric timings between the 471f005ef32Sjsg * even and odd field, while NTSC is symmetric. 472f005ef32Sjsg * 473f005ef32Sjsg * Moreover, if we want to create a progressive mode for 474f005ef32Sjsg * PAL, we need to use the odd field timings. 475f005ef32Sjsg * 476f005ef32Sjsg * Since odd == even for NTSC, we can just use the odd 477f005ef32Sjsg * one all the time to simplify the code a bit. 478f005ef32Sjsg */ 479f005ef32Sjsg vfp_min = params->vfp_lines.odd; 480f005ef32Sjsg vbp_min = params->vbp_lines.odd; 481f005ef32Sjsg vslen = params->vslen_lines.odd; 482f005ef32Sjsg } 483f005ef32Sjsg 484f005ef32Sjsg drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen); 485f005ef32Sjsg 486f005ef32Sjsg porches = params->num_lines - vactive - vslen; 487f005ef32Sjsg drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches); 488f005ef32Sjsg 489f005ef32Sjsg porches_rem = porches - vfp_min - vbp_min; 490f005ef32Sjsg vfp = vfp_min + (porches_rem / 2); 491f005ef32Sjsg drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp); 492f005ef32Sjsg 493f005ef32Sjsg vbp = porches - vfp; 494f005ef32Sjsg drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp); 495f005ef32Sjsg 496f005ef32Sjsg vtotal = vactive + vfp + vslen + vbp; 497f005ef32Sjsg if (params->num_lines != vtotal) { 498f005ef32Sjsg DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n", 499f005ef32Sjsg vtotal, params->num_lines); 500f005ef32Sjsg return -EINVAL; 501f005ef32Sjsg } 502f005ef32Sjsg 503f005ef32Sjsg mode->vdisplay = vactive; 504f005ef32Sjsg mode->vsync_start = mode->vdisplay + vfp; 505f005ef32Sjsg mode->vsync_end = mode->vsync_start + vslen; 506f005ef32Sjsg mode->vtotal = mode->vsync_end + vbp; 507f005ef32Sjsg 508f005ef32Sjsg if (mode->vtotal != params->num_lines) 509f005ef32Sjsg return -EINVAL; 510f005ef32Sjsg 511f005ef32Sjsg mode->type = DRM_MODE_TYPE_DRIVER; 512f005ef32Sjsg mode->flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC; 513f005ef32Sjsg if (interlace) 514f005ef32Sjsg mode->flags |= DRM_MODE_FLAG_INTERLACE; 515f005ef32Sjsg 516f005ef32Sjsg drm_mode_set_name(mode); 517f005ef32Sjsg 518f005ef32Sjsg drm_dbg_kms(dev, "Generated mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); 519f005ef32Sjsg 520f005ef32Sjsg return 0; 521f005ef32Sjsg } 522f005ef32Sjsg 523f005ef32Sjsg /** 524f005ef32Sjsg * drm_analog_tv_mode - create a display mode for an analog TV 525f005ef32Sjsg * @dev: drm device 526f005ef32Sjsg * @tv_mode: TV Mode standard to create a mode for. See DRM_MODE_TV_MODE_*. 527f005ef32Sjsg * @pixel_clock_hz: Pixel Clock Frequency, in Hertz 528f005ef32Sjsg * @hdisplay: hdisplay size 529f005ef32Sjsg * @vdisplay: vdisplay size 530f005ef32Sjsg * @interlace: whether to compute an interlaced mode 531f005ef32Sjsg * 532f005ef32Sjsg * This function creates a struct drm_display_mode instance suited for 533f005ef32Sjsg * an analog TV output, for one of the usual analog TV mode. 534f005ef32Sjsg * 535f005ef32Sjsg * Note that @hdisplay is larger than the usual constraints for the PAL 536f005ef32Sjsg * and NTSC timings, and we'll choose to ignore most timings constraints 537f005ef32Sjsg * to reach those resolutions. 538f005ef32Sjsg * 539f005ef32Sjsg * Returns: 540f005ef32Sjsg * 541f005ef32Sjsg * A pointer to the mode, allocated with drm_mode_create(). Returns NULL 542f005ef32Sjsg * on error. 543f005ef32Sjsg */ 544f005ef32Sjsg struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, 545f005ef32Sjsg enum drm_connector_tv_mode tv_mode, 546f005ef32Sjsg unsigned long pixel_clock_hz, 547f005ef32Sjsg unsigned int hdisplay, 548f005ef32Sjsg unsigned int vdisplay, 549f005ef32Sjsg bool interlace) 550f005ef32Sjsg { 551f005ef32Sjsg struct drm_display_mode *mode; 552f005ef32Sjsg enum drm_mode_analog analog; 553f005ef32Sjsg int ret; 554f005ef32Sjsg 555f005ef32Sjsg switch (tv_mode) { 556f005ef32Sjsg case DRM_MODE_TV_MODE_NTSC: 557f005ef32Sjsg fallthrough; 558f005ef32Sjsg case DRM_MODE_TV_MODE_NTSC_443: 559f005ef32Sjsg fallthrough; 560f005ef32Sjsg case DRM_MODE_TV_MODE_NTSC_J: 561f005ef32Sjsg fallthrough; 562f005ef32Sjsg case DRM_MODE_TV_MODE_PAL_M: 563f005ef32Sjsg analog = DRM_MODE_ANALOG_NTSC; 564f005ef32Sjsg break; 565f005ef32Sjsg 566f005ef32Sjsg case DRM_MODE_TV_MODE_PAL: 567f005ef32Sjsg fallthrough; 568f005ef32Sjsg case DRM_MODE_TV_MODE_PAL_N: 569f005ef32Sjsg fallthrough; 570f005ef32Sjsg case DRM_MODE_TV_MODE_SECAM: 571f005ef32Sjsg analog = DRM_MODE_ANALOG_PAL; 572f005ef32Sjsg break; 573f005ef32Sjsg 574f005ef32Sjsg default: 575f005ef32Sjsg return NULL; 576f005ef32Sjsg } 577f005ef32Sjsg 578f005ef32Sjsg mode = drm_mode_create(dev); 579f005ef32Sjsg if (!mode) 580f005ef32Sjsg return NULL; 581f005ef32Sjsg 582f005ef32Sjsg ret = fill_analog_mode(dev, mode, 583f005ef32Sjsg &tv_modes_parameters[analog], 584f005ef32Sjsg pixel_clock_hz, hdisplay, vdisplay, interlace); 585f005ef32Sjsg if (ret) 586f005ef32Sjsg goto err_free_mode; 587f005ef32Sjsg 588f005ef32Sjsg return mode; 589f005ef32Sjsg 590f005ef32Sjsg err_free_mode: 591f005ef32Sjsg drm_mode_destroy(dev, mode); 592f005ef32Sjsg return NULL; 593f005ef32Sjsg } 594f005ef32Sjsg EXPORT_SYMBOL(drm_analog_tv_mode); 595f005ef32Sjsg 5963253c27bSkettenis /** 5973253c27bSkettenis * drm_cvt_mode -create a modeline based on the CVT algorithm 5983253c27bSkettenis * @dev: drm device 599746fbbdbSjsg * @hdisplay: hdisplay size 600746fbbdbSjsg * @vdisplay: vdisplay size 601746fbbdbSjsg * @vrefresh: vrefresh rate 6023253c27bSkettenis * @reduced: whether to use reduced blanking 6033253c27bSkettenis * @interlaced: whether to compute an interlaced mode 6043253c27bSkettenis * @margins: whether to add margins (borders) 605746fbbdbSjsg * 606746fbbdbSjsg * This function is called to generate the modeline based on CVT algorithm 607746fbbdbSjsg * according to the hdisplay, vdisplay, vrefresh. 608746fbbdbSjsg * It is based from the VESA(TM) Coordinated Video Timing Generator by 609746fbbdbSjsg * Graham Loveridge April 9, 2003 available at 610746fbbdbSjsg * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls 611746fbbdbSjsg * 612746fbbdbSjsg * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. 613746fbbdbSjsg * What I have done is to translate it by using integer calculation. 6143253c27bSkettenis * 6153253c27bSkettenis * Returns: 6163253c27bSkettenis * The modeline based on the CVT algorithm stored in a drm_display_mode object. 6173253c27bSkettenis * The display mode object is allocated with drm_mode_create(). Returns NULL 6183253c27bSkettenis * when no mode could be allocated. 619746fbbdbSjsg */ 620d7873f4eSjsg struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, 621746fbbdbSjsg int vdisplay, int vrefresh, 622746fbbdbSjsg bool reduced, bool interlaced, bool margins) 623746fbbdbSjsg { 6243253c27bSkettenis #define HV_FACTOR 1000 625746fbbdbSjsg /* 1) top/bottom margin size (% of height) - default: 1.8, */ 626746fbbdbSjsg #define CVT_MARGIN_PERCENTAGE 18 627746fbbdbSjsg /* 2) character cell horizontal granularity (pixels) - default 8 */ 628746fbbdbSjsg #define CVT_H_GRANULARITY 8 629746fbbdbSjsg /* 3) Minimum vertical porch (lines) - default 3 */ 630746fbbdbSjsg #define CVT_MIN_V_PORCH 3 631746fbbdbSjsg /* 4) Minimum number of vertical back porch lines - default 6 */ 632746fbbdbSjsg #define CVT_MIN_V_BPORCH 6 633746fbbdbSjsg /* Pixel Clock step (kHz) */ 634746fbbdbSjsg #define CVT_CLOCK_STEP 250 635746fbbdbSjsg struct drm_display_mode *drm_mode; 636746fbbdbSjsg unsigned int vfieldrate, hperiod; 637746fbbdbSjsg int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; 638746fbbdbSjsg int interlace; 6397f4dd379Sjsg u64 tmp; 640746fbbdbSjsg 641c349dbc7Sjsg if (!hdisplay || !vdisplay) 642c349dbc7Sjsg return NULL; 643c349dbc7Sjsg 644746fbbdbSjsg /* allocate the drm_display_mode structure. If failure, we will 645746fbbdbSjsg * return directly 646746fbbdbSjsg */ 647746fbbdbSjsg drm_mode = drm_mode_create(dev); 648746fbbdbSjsg if (!drm_mode) 649746fbbdbSjsg return NULL; 650746fbbdbSjsg 651746fbbdbSjsg /* the CVT default refresh rate is 60Hz */ 652746fbbdbSjsg if (!vrefresh) 653746fbbdbSjsg vrefresh = 60; 654746fbbdbSjsg 655746fbbdbSjsg /* the required field fresh rate */ 656746fbbdbSjsg if (interlaced) 657746fbbdbSjsg vfieldrate = vrefresh * 2; 658746fbbdbSjsg else 659746fbbdbSjsg vfieldrate = vrefresh; 660746fbbdbSjsg 661746fbbdbSjsg /* horizontal pixels */ 662746fbbdbSjsg hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY); 663746fbbdbSjsg 664746fbbdbSjsg /* determine the left&right borders */ 665746fbbdbSjsg hmargin = 0; 666746fbbdbSjsg if (margins) { 667746fbbdbSjsg hmargin = hdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; 668746fbbdbSjsg hmargin -= hmargin % CVT_H_GRANULARITY; 669746fbbdbSjsg } 670746fbbdbSjsg /* find the total active pixels */ 671746fbbdbSjsg drm_mode->hdisplay = hdisplay_rnd + 2 * hmargin; 672746fbbdbSjsg 673746fbbdbSjsg /* find the number of lines per field */ 674746fbbdbSjsg if (interlaced) 675746fbbdbSjsg vdisplay_rnd = vdisplay / 2; 676746fbbdbSjsg else 677746fbbdbSjsg vdisplay_rnd = vdisplay; 678746fbbdbSjsg 679746fbbdbSjsg /* find the top & bottom borders */ 680746fbbdbSjsg vmargin = 0; 681746fbbdbSjsg if (margins) 682746fbbdbSjsg vmargin = vdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; 683746fbbdbSjsg 684746fbbdbSjsg drm_mode->vdisplay = vdisplay + 2 * vmargin; 685746fbbdbSjsg 686746fbbdbSjsg /* Interlaced */ 687746fbbdbSjsg if (interlaced) 688746fbbdbSjsg interlace = 1; 689746fbbdbSjsg else 690746fbbdbSjsg interlace = 0; 691746fbbdbSjsg 692746fbbdbSjsg /* Determine VSync Width from aspect ratio */ 693746fbbdbSjsg if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) 694746fbbdbSjsg vsync = 4; 695746fbbdbSjsg else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) 696746fbbdbSjsg vsync = 5; 697746fbbdbSjsg else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) 698746fbbdbSjsg vsync = 6; 699746fbbdbSjsg else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) 700746fbbdbSjsg vsync = 7; 701746fbbdbSjsg else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) 702746fbbdbSjsg vsync = 7; 703746fbbdbSjsg else /* custom */ 704746fbbdbSjsg vsync = 10; 705746fbbdbSjsg 706746fbbdbSjsg if (!reduced) { 707746fbbdbSjsg /* simplify the GTF calculation */ 708746fbbdbSjsg /* 4) Minimum time of vertical sync + back porch interval (µs) 709746fbbdbSjsg * default 550.0 710746fbbdbSjsg */ 711746fbbdbSjsg int tmp1, tmp2; 712746fbbdbSjsg #define CVT_MIN_VSYNC_BP 550 713746fbbdbSjsg /* 3) Nominal HSync width (% of line period) - default 8 */ 714746fbbdbSjsg #define CVT_HSYNC_PERCENTAGE 8 715746fbbdbSjsg unsigned int hblank_percentage; 716c349dbc7Sjsg int vsyncandback_porch, __maybe_unused vback_porch, hblank; 717746fbbdbSjsg 718746fbbdbSjsg /* estimated the horizontal period */ 719746fbbdbSjsg tmp1 = HV_FACTOR * 1000000 - 720746fbbdbSjsg CVT_MIN_VSYNC_BP * HV_FACTOR * vfieldrate; 721746fbbdbSjsg tmp2 = (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH) * 2 + 722746fbbdbSjsg interlace; 723746fbbdbSjsg hperiod = tmp1 * 2 / (tmp2 * vfieldrate); 724746fbbdbSjsg 725746fbbdbSjsg tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / hperiod + 1; 726746fbbdbSjsg /* 9. Find number of lines in sync + backporch */ 727746fbbdbSjsg if (tmp1 < (vsync + CVT_MIN_V_PORCH)) 728746fbbdbSjsg vsyncandback_porch = vsync + CVT_MIN_V_PORCH; 729746fbbdbSjsg else 730746fbbdbSjsg vsyncandback_porch = tmp1; 731746fbbdbSjsg /* 10. Find number of lines in back porch */ 732746fbbdbSjsg vback_porch = vsyncandback_porch - vsync; 733746fbbdbSjsg drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + 734746fbbdbSjsg vsyncandback_porch + CVT_MIN_V_PORCH; 735746fbbdbSjsg /* 5) Definition of Horizontal blanking time limitation */ 736746fbbdbSjsg /* Gradient (%/kHz) - default 600 */ 737746fbbdbSjsg #define CVT_M_FACTOR 600 738746fbbdbSjsg /* Offset (%) - default 40 */ 739746fbbdbSjsg #define CVT_C_FACTOR 40 740746fbbdbSjsg /* Blanking time scaling factor - default 128 */ 741746fbbdbSjsg #define CVT_K_FACTOR 128 742746fbbdbSjsg /* Scaling factor weighting - default 20 */ 743746fbbdbSjsg #define CVT_J_FACTOR 20 744746fbbdbSjsg #define CVT_M_PRIME (CVT_M_FACTOR * CVT_K_FACTOR / 256) 745746fbbdbSjsg #define CVT_C_PRIME ((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ 746746fbbdbSjsg CVT_J_FACTOR) 747746fbbdbSjsg /* 12. Find ideal blanking duty cycle from formula */ 748746fbbdbSjsg hblank_percentage = CVT_C_PRIME * HV_FACTOR - CVT_M_PRIME * 749746fbbdbSjsg hperiod / 1000; 750746fbbdbSjsg /* 13. Blanking time */ 751746fbbdbSjsg if (hblank_percentage < 20 * HV_FACTOR) 752746fbbdbSjsg hblank_percentage = 20 * HV_FACTOR; 753746fbbdbSjsg hblank = drm_mode->hdisplay * hblank_percentage / 754746fbbdbSjsg (100 * HV_FACTOR - hblank_percentage); 755746fbbdbSjsg hblank -= hblank % (2 * CVT_H_GRANULARITY); 7563253c27bSkettenis /* 14. find the total pixels per line */ 757746fbbdbSjsg drm_mode->htotal = drm_mode->hdisplay + hblank; 758746fbbdbSjsg drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2; 759746fbbdbSjsg drm_mode->hsync_start = drm_mode->hsync_end - 760746fbbdbSjsg (drm_mode->htotal * CVT_HSYNC_PERCENTAGE) / 100; 761746fbbdbSjsg drm_mode->hsync_start += CVT_H_GRANULARITY - 762746fbbdbSjsg drm_mode->hsync_start % CVT_H_GRANULARITY; 763746fbbdbSjsg /* fill the Vsync values */ 764746fbbdbSjsg drm_mode->vsync_start = drm_mode->vdisplay + CVT_MIN_V_PORCH; 765746fbbdbSjsg drm_mode->vsync_end = drm_mode->vsync_start + vsync; 766746fbbdbSjsg } else { 767746fbbdbSjsg /* Reduced blanking */ 768746fbbdbSjsg /* Minimum vertical blanking interval time (µs)- default 460 */ 769746fbbdbSjsg #define CVT_RB_MIN_VBLANK 460 770746fbbdbSjsg /* Fixed number of clocks for horizontal sync */ 771746fbbdbSjsg #define CVT_RB_H_SYNC 32 772746fbbdbSjsg /* Fixed number of clocks for horizontal blanking */ 773746fbbdbSjsg #define CVT_RB_H_BLANK 160 774746fbbdbSjsg /* Fixed number of lines for vertical front porch - default 3*/ 775746fbbdbSjsg #define CVT_RB_VFPORCH 3 776746fbbdbSjsg int vbilines; 777746fbbdbSjsg int tmp1, tmp2; 778746fbbdbSjsg /* 8. Estimate Horizontal period. */ 779746fbbdbSjsg tmp1 = HV_FACTOR * 1000000 - 780746fbbdbSjsg CVT_RB_MIN_VBLANK * HV_FACTOR * vfieldrate; 781746fbbdbSjsg tmp2 = vdisplay_rnd + 2 * vmargin; 782746fbbdbSjsg hperiod = tmp1 / (tmp2 * vfieldrate); 783746fbbdbSjsg /* 9. Find number of lines in vertical blanking */ 784746fbbdbSjsg vbilines = CVT_RB_MIN_VBLANK * HV_FACTOR / hperiod + 1; 785746fbbdbSjsg /* 10. Check if vertical blanking is sufficient */ 786746fbbdbSjsg if (vbilines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) 787746fbbdbSjsg vbilines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; 788746fbbdbSjsg /* 11. Find total number of lines in vertical field */ 789746fbbdbSjsg drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + vbilines; 790746fbbdbSjsg /* 12. Find total number of pixels in a line */ 791746fbbdbSjsg drm_mode->htotal = drm_mode->hdisplay + CVT_RB_H_BLANK; 792746fbbdbSjsg /* Fill in HSync values */ 793746fbbdbSjsg drm_mode->hsync_end = drm_mode->hdisplay + CVT_RB_H_BLANK / 2; 794746fbbdbSjsg drm_mode->hsync_start = drm_mode->hsync_end - CVT_RB_H_SYNC; 795746fbbdbSjsg /* Fill in VSync values */ 796746fbbdbSjsg drm_mode->vsync_start = drm_mode->vdisplay + CVT_RB_VFPORCH; 797746fbbdbSjsg drm_mode->vsync_end = drm_mode->vsync_start + vsync; 798746fbbdbSjsg } 799746fbbdbSjsg /* 15/13. Find pixel clock frequency (kHz for xf86) */ 8007f4dd379Sjsg tmp = drm_mode->htotal; /* perform intermediate calcs in u64 */ 8017f4dd379Sjsg tmp *= HV_FACTOR * 1000; 8027f4dd379Sjsg do_div(tmp, hperiod); 8037f4dd379Sjsg tmp -= drm_mode->clock % CVT_CLOCK_STEP; 8047f4dd379Sjsg drm_mode->clock = tmp; 805746fbbdbSjsg /* 18/16. Find actual vertical frame frequency */ 806746fbbdbSjsg /* ignore - just set the mode flag for interlaced */ 807746fbbdbSjsg if (interlaced) { 808746fbbdbSjsg drm_mode->vtotal *= 2; 809746fbbdbSjsg drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; 810746fbbdbSjsg } 811746fbbdbSjsg /* Fill the mode line name */ 812746fbbdbSjsg drm_mode_set_name(drm_mode); 813746fbbdbSjsg if (reduced) 814746fbbdbSjsg drm_mode->flags |= (DRM_MODE_FLAG_PHSYNC | 815746fbbdbSjsg DRM_MODE_FLAG_NVSYNC); 816746fbbdbSjsg else 817746fbbdbSjsg drm_mode->flags |= (DRM_MODE_FLAG_PVSYNC | 818746fbbdbSjsg DRM_MODE_FLAG_NHSYNC); 819746fbbdbSjsg 820746fbbdbSjsg return drm_mode; 821746fbbdbSjsg } 822746fbbdbSjsg EXPORT_SYMBOL(drm_cvt_mode); 823746fbbdbSjsg 824746fbbdbSjsg /** 8253253c27bSkettenis * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm 826746fbbdbSjsg * @dev: drm device 827746fbbdbSjsg * @hdisplay: hdisplay size 828746fbbdbSjsg * @vdisplay: vdisplay size 829746fbbdbSjsg * @vrefresh: vrefresh rate. 8303253c27bSkettenis * @interlaced: whether to compute an interlaced mode 8313253c27bSkettenis * @margins: desired margin (borders) size 8323253c27bSkettenis * @GTF_M: extended GTF formula parameters 8333253c27bSkettenis * @GTF_2C: extended GTF formula parameters 8343253c27bSkettenis * @GTF_K: extended GTF formula parameters 8353253c27bSkettenis * @GTF_2J: extended GTF formula parameters 836746fbbdbSjsg * 837746fbbdbSjsg * GTF feature blocks specify C and J in multiples of 0.5, so we pass them 838746fbbdbSjsg * in here multiplied by two. For a C of 40, pass in 80. 8393253c27bSkettenis * 8403253c27bSkettenis * Returns: 8413253c27bSkettenis * The modeline based on the full GTF algorithm stored in a drm_display_mode object. 8423253c27bSkettenis * The display mode object is allocated with drm_mode_create(). Returns NULL 8433253c27bSkettenis * when no mode could be allocated. 844746fbbdbSjsg */ 845746fbbdbSjsg struct drm_display_mode * 846746fbbdbSjsg drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, 847746fbbdbSjsg int vrefresh, bool interlaced, int margins, 848746fbbdbSjsg int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) 849746fbbdbSjsg { /* 1) top/bottom margin size (% of height) - default: 1.8, */ 850746fbbdbSjsg #define GTF_MARGIN_PERCENTAGE 18 851746fbbdbSjsg /* 2) character cell horizontal granularity (pixels) - default 8 */ 852746fbbdbSjsg #define GTF_CELL_GRAN 8 853746fbbdbSjsg /* 3) Minimum vertical porch (lines) - default 3 */ 854746fbbdbSjsg #define GTF_MIN_V_PORCH 1 855746fbbdbSjsg /* width of vsync in lines */ 856746fbbdbSjsg #define V_SYNC_RQD 3 857746fbbdbSjsg /* width of hsync as % of total line */ 858746fbbdbSjsg #define H_SYNC_PERCENT 8 859746fbbdbSjsg /* min time of vsync + back porch (microsec) */ 860746fbbdbSjsg #define MIN_VSYNC_PLUS_BP 550 861746fbbdbSjsg /* C' and M' are part of the Blanking Duty Cycle computation */ 862746fbbdbSjsg #define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) 863746fbbdbSjsg #define GTF_M_PRIME (GTF_K * GTF_M / 256) 864746fbbdbSjsg struct drm_display_mode *drm_mode; 865746fbbdbSjsg unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; 866746fbbdbSjsg int top_margin, bottom_margin; 867746fbbdbSjsg int interlace; 868746fbbdbSjsg unsigned int hfreq_est; 869c349dbc7Sjsg int vsync_plus_bp, __maybe_unused vback_porch; 870c349dbc7Sjsg unsigned int vtotal_lines, __maybe_unused vfieldrate_est; 871c349dbc7Sjsg unsigned int __maybe_unused hperiod; 872c349dbc7Sjsg unsigned int vfield_rate, __maybe_unused vframe_rate; 873746fbbdbSjsg int left_margin, right_margin; 874746fbbdbSjsg unsigned int total_active_pixels, ideal_duty_cycle; 875746fbbdbSjsg unsigned int hblank, total_pixels, pixel_freq; 876746fbbdbSjsg int hsync, hfront_porch, vodd_front_porch_lines; 877746fbbdbSjsg unsigned int tmp1, tmp2; 878746fbbdbSjsg 879c349dbc7Sjsg if (!hdisplay || !vdisplay) 880c349dbc7Sjsg return NULL; 881c349dbc7Sjsg 882746fbbdbSjsg drm_mode = drm_mode_create(dev); 883746fbbdbSjsg if (!drm_mode) 884746fbbdbSjsg return NULL; 885746fbbdbSjsg 886746fbbdbSjsg /* 1. In order to give correct results, the number of horizontal 887746fbbdbSjsg * pixels requested is first processed to ensure that it is divisible 888746fbbdbSjsg * by the character size, by rounding it to the nearest character 889746fbbdbSjsg * cell boundary: 890746fbbdbSjsg */ 891746fbbdbSjsg hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; 892746fbbdbSjsg hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN; 893746fbbdbSjsg 894746fbbdbSjsg /* 2. If interlace is requested, the number of vertical lines assumed 895746fbbdbSjsg * by the calculation must be halved, as the computation calculates 896746fbbdbSjsg * the number of vertical lines per field. 897746fbbdbSjsg */ 898746fbbdbSjsg if (interlaced) 899746fbbdbSjsg vdisplay_rnd = vdisplay / 2; 900746fbbdbSjsg else 901746fbbdbSjsg vdisplay_rnd = vdisplay; 902746fbbdbSjsg 903746fbbdbSjsg /* 3. Find the frame rate required: */ 904746fbbdbSjsg if (interlaced) 905746fbbdbSjsg vfieldrate_rqd = vrefresh * 2; 906746fbbdbSjsg else 907746fbbdbSjsg vfieldrate_rqd = vrefresh; 908746fbbdbSjsg 909746fbbdbSjsg /* 4. Find number of lines in Top margin: */ 910746fbbdbSjsg top_margin = 0; 911746fbbdbSjsg if (margins) 912746fbbdbSjsg top_margin = (vdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / 913746fbbdbSjsg 1000; 914746fbbdbSjsg /* 5. Find number of lines in bottom margin: */ 915746fbbdbSjsg bottom_margin = top_margin; 916746fbbdbSjsg 917746fbbdbSjsg /* 6. If interlace is required, then set variable interlace: */ 918746fbbdbSjsg if (interlaced) 919746fbbdbSjsg interlace = 1; 920746fbbdbSjsg else 921746fbbdbSjsg interlace = 0; 922746fbbdbSjsg 923746fbbdbSjsg /* 7. Estimate the Horizontal frequency */ 924746fbbdbSjsg { 925746fbbdbSjsg tmp1 = (1000000 - MIN_VSYNC_PLUS_BP * vfieldrate_rqd) / 500; 926746fbbdbSjsg tmp2 = (vdisplay_rnd + 2 * top_margin + GTF_MIN_V_PORCH) * 927746fbbdbSjsg 2 + interlace; 928746fbbdbSjsg hfreq_est = (tmp2 * 1000 * vfieldrate_rqd) / tmp1; 929746fbbdbSjsg } 930746fbbdbSjsg 931746fbbdbSjsg /* 8. Find the number of lines in V sync + back porch */ 932746fbbdbSjsg /* [V SYNC+BP] = RINT(([MIN VSYNC+BP] * hfreq_est / 1000000)) */ 933746fbbdbSjsg vsync_plus_bp = MIN_VSYNC_PLUS_BP * hfreq_est / 1000; 934746fbbdbSjsg vsync_plus_bp = (vsync_plus_bp + 500) / 1000; 935746fbbdbSjsg /* 9. Find the number of lines in V back porch alone: */ 936746fbbdbSjsg vback_porch = vsync_plus_bp - V_SYNC_RQD; 937746fbbdbSjsg /* 10. Find the total number of lines in Vertical field period: */ 938746fbbdbSjsg vtotal_lines = vdisplay_rnd + top_margin + bottom_margin + 939746fbbdbSjsg vsync_plus_bp + GTF_MIN_V_PORCH; 940746fbbdbSjsg /* 11. Estimate the Vertical field frequency: */ 941746fbbdbSjsg vfieldrate_est = hfreq_est / vtotal_lines; 942746fbbdbSjsg /* 12. Find the actual horizontal period: */ 943746fbbdbSjsg hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines); 944746fbbdbSjsg 945746fbbdbSjsg /* 13. Find the actual Vertical field frequency: */ 946746fbbdbSjsg vfield_rate = hfreq_est / vtotal_lines; 947746fbbdbSjsg /* 14. Find the Vertical frame frequency: */ 948746fbbdbSjsg if (interlaced) 949746fbbdbSjsg vframe_rate = vfield_rate / 2; 950746fbbdbSjsg else 951746fbbdbSjsg vframe_rate = vfield_rate; 952746fbbdbSjsg /* 15. Find number of pixels in left margin: */ 953746fbbdbSjsg if (margins) 954746fbbdbSjsg left_margin = (hdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / 955746fbbdbSjsg 1000; 956746fbbdbSjsg else 957746fbbdbSjsg left_margin = 0; 958746fbbdbSjsg 959746fbbdbSjsg /* 16.Find number of pixels in right margin: */ 960746fbbdbSjsg right_margin = left_margin; 961746fbbdbSjsg /* 17.Find total number of active pixels in image and left and right */ 962746fbbdbSjsg total_active_pixels = hdisplay_rnd + left_margin + right_margin; 963746fbbdbSjsg /* 18.Find the ideal blanking duty cycle from blanking duty cycle */ 964746fbbdbSjsg ideal_duty_cycle = GTF_C_PRIME * 1000 - 965746fbbdbSjsg (GTF_M_PRIME * 1000000 / hfreq_est); 966746fbbdbSjsg /* 19.Find the number of pixels in the blanking time to the nearest 967746fbbdbSjsg * double character cell: */ 968746fbbdbSjsg hblank = total_active_pixels * ideal_duty_cycle / 969746fbbdbSjsg (100000 - ideal_duty_cycle); 970746fbbdbSjsg hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN); 971746fbbdbSjsg hblank = hblank * 2 * GTF_CELL_GRAN; 972746fbbdbSjsg /* 20.Find total number of pixels: */ 973746fbbdbSjsg total_pixels = total_active_pixels + hblank; 974746fbbdbSjsg /* 21.Find pixel clock frequency: */ 975746fbbdbSjsg pixel_freq = total_pixels * hfreq_est / 1000; 976746fbbdbSjsg /* Stage 1 computations are now complete; I should really pass 977746fbbdbSjsg * the results to another function and do the Stage 2 computations, 978746fbbdbSjsg * but I only need a few more values so I'll just append the 979746fbbdbSjsg * computations here for now */ 980746fbbdbSjsg /* 17. Find the number of pixels in the horizontal sync period: */ 981746fbbdbSjsg hsync = H_SYNC_PERCENT * total_pixels / 100; 982746fbbdbSjsg hsync = (hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; 983746fbbdbSjsg hsync = hsync * GTF_CELL_GRAN; 984746fbbdbSjsg /* 18. Find the number of pixels in horizontal front porch period */ 985746fbbdbSjsg hfront_porch = hblank / 2 - hsync; 986746fbbdbSjsg /* 36. Find the number of lines in the odd front porch period: */ 987746fbbdbSjsg vodd_front_porch_lines = GTF_MIN_V_PORCH ; 988746fbbdbSjsg 989746fbbdbSjsg /* finally, pack the results in the mode struct */ 990746fbbdbSjsg drm_mode->hdisplay = hdisplay_rnd; 991746fbbdbSjsg drm_mode->hsync_start = hdisplay_rnd + hfront_porch; 992746fbbdbSjsg drm_mode->hsync_end = drm_mode->hsync_start + hsync; 993746fbbdbSjsg drm_mode->htotal = total_pixels; 994746fbbdbSjsg drm_mode->vdisplay = vdisplay_rnd; 995746fbbdbSjsg drm_mode->vsync_start = vdisplay_rnd + vodd_front_porch_lines; 996746fbbdbSjsg drm_mode->vsync_end = drm_mode->vsync_start + V_SYNC_RQD; 997746fbbdbSjsg drm_mode->vtotal = vtotal_lines; 998746fbbdbSjsg 999746fbbdbSjsg drm_mode->clock = pixel_freq; 1000746fbbdbSjsg 1001746fbbdbSjsg if (interlaced) { 1002746fbbdbSjsg drm_mode->vtotal *= 2; 1003746fbbdbSjsg drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; 1004746fbbdbSjsg } 1005746fbbdbSjsg 1006746fbbdbSjsg drm_mode_set_name(drm_mode); 1007746fbbdbSjsg if (GTF_M == 600 && GTF_2C == 80 && GTF_K == 128 && GTF_2J == 40) 1008746fbbdbSjsg drm_mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC; 1009746fbbdbSjsg else 1010746fbbdbSjsg drm_mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; 1011746fbbdbSjsg 1012746fbbdbSjsg return drm_mode; 1013746fbbdbSjsg } 1014746fbbdbSjsg EXPORT_SYMBOL(drm_gtf_mode_complex); 1015746fbbdbSjsg 1016746fbbdbSjsg /** 10173253c27bSkettenis * drm_gtf_mode - create the modeline based on the GTF algorithm 1018746fbbdbSjsg * @dev: drm device 1019746fbbdbSjsg * @hdisplay: hdisplay size 1020746fbbdbSjsg * @vdisplay: vdisplay size 1021746fbbdbSjsg * @vrefresh: vrefresh rate. 10223253c27bSkettenis * @interlaced: whether to compute an interlaced mode 10233253c27bSkettenis * @margins: desired margin (borders) size 1024746fbbdbSjsg * 1025746fbbdbSjsg * return the modeline based on GTF algorithm 1026746fbbdbSjsg * 1027746fbbdbSjsg * This function is to create the modeline based on the GTF algorithm. 1028746fbbdbSjsg * Generalized Timing Formula is derived from: 10297f4dd379Sjsg * 1030746fbbdbSjsg * GTF Spreadsheet by Andy Morrish (1/5/97) 1031ad8b1aafSjsg * available at https://www.vesa.org 1032746fbbdbSjsg * 1033746fbbdbSjsg * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. 1034746fbbdbSjsg * What I have done is to translate it by using integer calculation. 1035746fbbdbSjsg * I also refer to the function of fb_get_mode in the file of 1036746fbbdbSjsg * drivers/video/fbmon.c 1037746fbbdbSjsg * 10387f4dd379Sjsg * Standard GTF parameters:: 10397f4dd379Sjsg * 1040746fbbdbSjsg * M = 600 1041746fbbdbSjsg * C = 40 1042746fbbdbSjsg * K = 128 1043746fbbdbSjsg * J = 20 10443253c27bSkettenis * 10453253c27bSkettenis * Returns: 10463253c27bSkettenis * The modeline based on the GTF algorithm stored in a drm_display_mode object. 10473253c27bSkettenis * The display mode object is allocated with drm_mode_create(). Returns NULL 10483253c27bSkettenis * when no mode could be allocated. 1049746fbbdbSjsg */ 1050746fbbdbSjsg struct drm_display_mode * 1051746fbbdbSjsg drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, 10523253c27bSkettenis bool interlaced, int margins) 1053746fbbdbSjsg { 10543253c27bSkettenis return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, 10553253c27bSkettenis interlaced, margins, 10563253c27bSkettenis 600, 40 * 2, 128, 20 * 2); 1057746fbbdbSjsg } 1058746fbbdbSjsg EXPORT_SYMBOL(drm_gtf_mode); 1059746fbbdbSjsg 1060e1001332Skettenis #ifdef CONFIG_VIDEOMODE_HELPERS 10613253c27bSkettenis /** 10623253c27bSkettenis * drm_display_mode_from_videomode - fill in @dmode using @vm, 10633253c27bSkettenis * @vm: videomode structure to use as source 10643253c27bSkettenis * @dmode: drm_display_mode structure to use as destination 10653253c27bSkettenis * 10663253c27bSkettenis * Fills out @dmode using the display mode specified in @vm. 10673253c27bSkettenis */ 10683253c27bSkettenis void drm_display_mode_from_videomode(const struct videomode *vm, 1069e1001332Skettenis struct drm_display_mode *dmode) 1070e1001332Skettenis { 1071e1001332Skettenis dmode->hdisplay = vm->hactive; 1072e1001332Skettenis dmode->hsync_start = dmode->hdisplay + vm->hfront_porch; 1073e1001332Skettenis dmode->hsync_end = dmode->hsync_start + vm->hsync_len; 1074e1001332Skettenis dmode->htotal = dmode->hsync_end + vm->hback_porch; 1075e1001332Skettenis 1076e1001332Skettenis dmode->vdisplay = vm->vactive; 1077e1001332Skettenis dmode->vsync_start = dmode->vdisplay + vm->vfront_porch; 1078e1001332Skettenis dmode->vsync_end = dmode->vsync_start + vm->vsync_len; 1079e1001332Skettenis dmode->vtotal = dmode->vsync_end + vm->vback_porch; 1080e1001332Skettenis 1081e1001332Skettenis dmode->clock = vm->pixelclock / 1000; 1082e1001332Skettenis 1083e1001332Skettenis dmode->flags = 0; 1084e1001332Skettenis if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) 1085e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_PHSYNC; 1086e1001332Skettenis else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) 1087e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_NHSYNC; 1088e1001332Skettenis if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) 1089e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_PVSYNC; 1090e1001332Skettenis else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) 1091e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_NVSYNC; 1092e1001332Skettenis if (vm->flags & DISPLAY_FLAGS_INTERLACED) 1093e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_INTERLACE; 1094e1001332Skettenis if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) 1095e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_DBLSCAN; 1096e1001332Skettenis if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) 1097e1001332Skettenis dmode->flags |= DRM_MODE_FLAG_DBLCLK; 1098e1001332Skettenis drm_mode_set_name(dmode); 1099e1001332Skettenis } 1100e1001332Skettenis EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); 1101e1001332Skettenis 11023253c27bSkettenis /** 11033253c27bSkettenis * drm_display_mode_to_videomode - fill in @vm using @dmode, 11043253c27bSkettenis * @dmode: drm_display_mode structure to use as source 11053253c27bSkettenis * @vm: videomode structure to use as destination 11063253c27bSkettenis * 11073253c27bSkettenis * Fills out @vm using the display mode specified in @dmode. 11083253c27bSkettenis */ 11093253c27bSkettenis void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, 11103253c27bSkettenis struct videomode *vm) 11113253c27bSkettenis { 11123253c27bSkettenis vm->hactive = dmode->hdisplay; 11133253c27bSkettenis vm->hfront_porch = dmode->hsync_start - dmode->hdisplay; 11143253c27bSkettenis vm->hsync_len = dmode->hsync_end - dmode->hsync_start; 11153253c27bSkettenis vm->hback_porch = dmode->htotal - dmode->hsync_end; 11163253c27bSkettenis 11173253c27bSkettenis vm->vactive = dmode->vdisplay; 11183253c27bSkettenis vm->vfront_porch = dmode->vsync_start - dmode->vdisplay; 11193253c27bSkettenis vm->vsync_len = dmode->vsync_end - dmode->vsync_start; 11203253c27bSkettenis vm->vback_porch = dmode->vtotal - dmode->vsync_end; 11213253c27bSkettenis 11223253c27bSkettenis vm->pixelclock = dmode->clock * 1000; 11233253c27bSkettenis 11243253c27bSkettenis vm->flags = 0; 11253253c27bSkettenis if (dmode->flags & DRM_MODE_FLAG_PHSYNC) 11263253c27bSkettenis vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; 11273253c27bSkettenis else if (dmode->flags & DRM_MODE_FLAG_NHSYNC) 11283253c27bSkettenis vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; 11293253c27bSkettenis if (dmode->flags & DRM_MODE_FLAG_PVSYNC) 11303253c27bSkettenis vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; 11313253c27bSkettenis else if (dmode->flags & DRM_MODE_FLAG_NVSYNC) 11323253c27bSkettenis vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; 11333253c27bSkettenis if (dmode->flags & DRM_MODE_FLAG_INTERLACE) 11343253c27bSkettenis vm->flags |= DISPLAY_FLAGS_INTERLACED; 11353253c27bSkettenis if (dmode->flags & DRM_MODE_FLAG_DBLSCAN) 11363253c27bSkettenis vm->flags |= DISPLAY_FLAGS_DOUBLESCAN; 11373253c27bSkettenis if (dmode->flags & DRM_MODE_FLAG_DBLCLK) 11383253c27bSkettenis vm->flags |= DISPLAY_FLAGS_DOUBLECLK; 11393253c27bSkettenis } 11403253c27bSkettenis EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); 11413253c27bSkettenis 11427f4dd379Sjsg /** 11437f4dd379Sjsg * drm_bus_flags_from_videomode - extract information about pixelclk and 11447f4dd379Sjsg * DE polarity from videomode and store it in a separate variable 11457f4dd379Sjsg * @vm: videomode structure to use 11467f4dd379Sjsg * @bus_flags: information about pixelclk, sync and DE polarity will be stored 11477f4dd379Sjsg * here 11487f4dd379Sjsg * 1149c349dbc7Sjsg * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE 1150c349dbc7Sjsg * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS 11517f4dd379Sjsg * found in @vm 11527f4dd379Sjsg */ 11537f4dd379Sjsg void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) 11547f4dd379Sjsg { 11557f4dd379Sjsg *bus_flags = 0; 11567f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 1157c349dbc7Sjsg *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; 11587f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 1159c349dbc7Sjsg *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 11607f4dd379Sjsg 11617f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) 1162c349dbc7Sjsg *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; 11637f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) 1164c349dbc7Sjsg *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; 11657f4dd379Sjsg 11667f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_DE_LOW) 11677f4dd379Sjsg *bus_flags |= DRM_BUS_FLAG_DE_LOW; 11687f4dd379Sjsg if (vm->flags & DISPLAY_FLAGS_DE_HIGH) 11697f4dd379Sjsg *bus_flags |= DRM_BUS_FLAG_DE_HIGH; 11707f4dd379Sjsg } 11717f4dd379Sjsg EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); 11727f4dd379Sjsg 1173e1001332Skettenis #ifdef CONFIG_OF 1174e1001332Skettenis /** 1175e1001332Skettenis * of_get_drm_display_mode - get a drm_display_mode from devicetree 1176e1001332Skettenis * @np: device_node with the timing specification 1177e1001332Skettenis * @dmode: will be set to the return value 11787f4dd379Sjsg * @bus_flags: information about pixelclk, sync and DE polarity 1179e1001332Skettenis * @index: index into the list of display timings in devicetree 1180e1001332Skettenis * 1181e1001332Skettenis * This function is expensive and should only be used, if only one mode is to be 1182e1001332Skettenis * read from DT. To get multiple modes start with of_get_display_timings and 1183e1001332Skettenis * work with that instead. 11843253c27bSkettenis * 11853253c27bSkettenis * Returns: 11863253c27bSkettenis * 0 on success, a negative errno code when no of videomode node was found. 1187e1001332Skettenis */ 1188e1001332Skettenis int of_get_drm_display_mode(struct device_node *np, 11897f4dd379Sjsg struct drm_display_mode *dmode, u32 *bus_flags, 11907f4dd379Sjsg int index) 1191e1001332Skettenis { 1192e1001332Skettenis struct videomode vm; 1193e1001332Skettenis int ret; 1194e1001332Skettenis 1195e1001332Skettenis ret = of_get_videomode(np, &vm, index); 1196e1001332Skettenis if (ret) 1197e1001332Skettenis return ret; 1198e1001332Skettenis 1199e1001332Skettenis drm_display_mode_from_videomode(&vm, dmode); 12007f4dd379Sjsg if (bus_flags) 12017f4dd379Sjsg drm_bus_flags_from_videomode(&vm, bus_flags); 1202e1001332Skettenis 1203c349dbc7Sjsg pr_debug("%pOF: got %dx%d display mode\n", 1204c349dbc7Sjsg np, vm.hactive, vm.vactive); 1205e1001332Skettenis drm_mode_debug_printmodeline(dmode); 1206e1001332Skettenis 1207e1001332Skettenis return 0; 1208e1001332Skettenis } 1209e1001332Skettenis EXPORT_SYMBOL_GPL(of_get_drm_display_mode); 12101bb76ff1Sjsg 12111bb76ff1Sjsg /** 12121bb76ff1Sjsg * of_get_drm_panel_display_mode - get a panel-timing drm_display_mode from devicetree 12131bb76ff1Sjsg * @np: device_node with the panel-timing specification 12141bb76ff1Sjsg * @dmode: will be set to the return value 12151bb76ff1Sjsg * @bus_flags: information about pixelclk, sync and DE polarity 12161bb76ff1Sjsg * 12171bb76ff1Sjsg * The mandatory Device Tree properties width-mm and height-mm 12181bb76ff1Sjsg * are read and set on the display mode. 12191bb76ff1Sjsg * 12201bb76ff1Sjsg * Returns: 12211bb76ff1Sjsg * Zero on success, negative error code on failure. 12221bb76ff1Sjsg */ 12231bb76ff1Sjsg int of_get_drm_panel_display_mode(struct device_node *np, 12241bb76ff1Sjsg struct drm_display_mode *dmode, u32 *bus_flags) 12251bb76ff1Sjsg { 12261bb76ff1Sjsg u32 width_mm = 0, height_mm = 0; 12271bb76ff1Sjsg struct display_timing timing; 12281bb76ff1Sjsg struct videomode vm; 12291bb76ff1Sjsg int ret; 12301bb76ff1Sjsg 12311bb76ff1Sjsg ret = of_get_display_timing(np, "panel-timing", &timing); 12321bb76ff1Sjsg if (ret) 12331bb76ff1Sjsg return ret; 12341bb76ff1Sjsg 12351bb76ff1Sjsg videomode_from_timing(&timing, &vm); 12361bb76ff1Sjsg 12371bb76ff1Sjsg memset(dmode, 0, sizeof(*dmode)); 12381bb76ff1Sjsg drm_display_mode_from_videomode(&vm, dmode); 12391bb76ff1Sjsg if (bus_flags) 12401bb76ff1Sjsg drm_bus_flags_from_videomode(&vm, bus_flags); 12411bb76ff1Sjsg 12421bb76ff1Sjsg ret = of_property_read_u32(np, "width-mm", &width_mm); 12431bb76ff1Sjsg if (ret) 12441bb76ff1Sjsg return ret; 12451bb76ff1Sjsg 12461bb76ff1Sjsg ret = of_property_read_u32(np, "height-mm", &height_mm); 12471bb76ff1Sjsg if (ret) 12481bb76ff1Sjsg return ret; 12491bb76ff1Sjsg 12501bb76ff1Sjsg dmode->width_mm = width_mm; 12511bb76ff1Sjsg dmode->height_mm = height_mm; 12521bb76ff1Sjsg 12531bb76ff1Sjsg drm_mode_debug_printmodeline(dmode); 12541bb76ff1Sjsg 12551bb76ff1Sjsg return 0; 12561bb76ff1Sjsg } 12571bb76ff1Sjsg EXPORT_SYMBOL_GPL(of_get_drm_panel_display_mode); 1258e1001332Skettenis #endif /* CONFIG_OF */ 1259e1001332Skettenis #endif /* CONFIG_VIDEOMODE_HELPERS */ 1260e1001332Skettenis 1261746fbbdbSjsg /** 1262746fbbdbSjsg * drm_mode_set_name - set the name on a mode 1263746fbbdbSjsg * @mode: name will be set in this mode 1264746fbbdbSjsg * 12653253c27bSkettenis * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay> 12663253c27bSkettenis * with an optional 'i' suffix for interlaced modes. 1267746fbbdbSjsg */ 1268d7873f4eSjsg void drm_mode_set_name(struct drm_display_mode *mode) 1269746fbbdbSjsg { 1270746fbbdbSjsg bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 1271746fbbdbSjsg 1272746fbbdbSjsg snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s", 1273746fbbdbSjsg mode->hdisplay, mode->vdisplay, 1274746fbbdbSjsg interlaced ? "i" : ""); 1275746fbbdbSjsg } 1276746fbbdbSjsg EXPORT_SYMBOL(drm_mode_set_name); 1277746fbbdbSjsg 12787f4dd379Sjsg /** 1279746fbbdbSjsg * drm_mode_vrefresh - get the vrefresh of a mode 1280746fbbdbSjsg * @mode: mode 1281746fbbdbSjsg * 12823253c27bSkettenis * Returns: 12833253c27bSkettenis * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the 12843253c27bSkettenis * value first if it is not yet set. 1285746fbbdbSjsg */ 1286d7873f4eSjsg int drm_mode_vrefresh(const struct drm_display_mode *mode) 1287746fbbdbSjsg { 1288*54caf09aSjsg unsigned int num = 1, den = 1; 12897f4dd379Sjsg 1290ad8b1aafSjsg if (mode->htotal == 0 || mode->vtotal == 0) 1291ad8b1aafSjsg return 0; 1292ad8b1aafSjsg 1293746fbbdbSjsg if (mode->flags & DRM_MODE_FLAG_INTERLACE) 12947f4dd379Sjsg num *= 2; 1295746fbbdbSjsg if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 12967f4dd379Sjsg den *= 2; 1297746fbbdbSjsg if (mode->vscan > 1) 12987f4dd379Sjsg den *= mode->vscan; 12997f4dd379Sjsg 1300*54caf09aSjsg if (check_mul_overflow(mode->clock, num, &num)) 1301*54caf09aSjsg return 0; 1302*54caf09aSjsg 1303*54caf09aSjsg if (check_mul_overflow(mode->htotal * mode->vtotal, den, &den)) 1304*54caf09aSjsg return 0; 1305*54caf09aSjsg 1306ad8b1aafSjsg return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den); 1307746fbbdbSjsg } 1308746fbbdbSjsg EXPORT_SYMBOL(drm_mode_vrefresh); 1309746fbbdbSjsg 1310746fbbdbSjsg /** 13117f4dd379Sjsg * drm_mode_get_hv_timing - Fetches hdisplay/vdisplay for given mode 13127f4dd379Sjsg * @mode: mode to query 13137f4dd379Sjsg * @hdisplay: hdisplay value to fill in 13147f4dd379Sjsg * @vdisplay: vdisplay value to fill in 13157f4dd379Sjsg * 13167f4dd379Sjsg * The vdisplay value will be doubled if the specified mode is a stereo mode of 13177f4dd379Sjsg * the appropriate layout. 13187f4dd379Sjsg */ 13197f4dd379Sjsg void drm_mode_get_hv_timing(const struct drm_display_mode *mode, 13207f4dd379Sjsg int *hdisplay, int *vdisplay) 13217f4dd379Sjsg { 13221bb76ff1Sjsg struct drm_display_mode adjusted; 13231bb76ff1Sjsg 13241bb76ff1Sjsg drm_mode_init(&adjusted, mode); 13257f4dd379Sjsg 13267f4dd379Sjsg drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); 13277f4dd379Sjsg *hdisplay = adjusted.crtc_hdisplay; 13287f4dd379Sjsg *vdisplay = adjusted.crtc_vdisplay; 13297f4dd379Sjsg } 13307f4dd379Sjsg EXPORT_SYMBOL(drm_mode_get_hv_timing); 13317f4dd379Sjsg 13327f4dd379Sjsg /** 13333253c27bSkettenis * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters 1334746fbbdbSjsg * @p: mode 1335e1001332Skettenis * @adjust_flags: a combination of adjustment flags 1336746fbbdbSjsg * 13373253c27bSkettenis * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. 1338e1001332Skettenis * 1339e1001332Skettenis * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of 1340e1001332Skettenis * interlaced modes. 1341e1001332Skettenis * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for 1342e1001332Skettenis * buffers containing two eyes (only adjust the timings when needed, eg. for 1343e1001332Skettenis * "frame packing" or "side by side full"). 13443253c27bSkettenis * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* 13453253c27bSkettenis * be performed for doublescan and vscan > 1 modes respectively. 1346746fbbdbSjsg */ 1347d7873f4eSjsg void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) 1348746fbbdbSjsg { 13497f4dd379Sjsg if (!p) 1350746fbbdbSjsg return; 1351746fbbdbSjsg 1352e1001332Skettenis p->crtc_clock = p->clock; 1353746fbbdbSjsg p->crtc_hdisplay = p->hdisplay; 1354746fbbdbSjsg p->crtc_hsync_start = p->hsync_start; 1355746fbbdbSjsg p->crtc_hsync_end = p->hsync_end; 1356746fbbdbSjsg p->crtc_htotal = p->htotal; 1357746fbbdbSjsg p->crtc_hskew = p->hskew; 1358746fbbdbSjsg p->crtc_vdisplay = p->vdisplay; 1359746fbbdbSjsg p->crtc_vsync_start = p->vsync_start; 1360746fbbdbSjsg p->crtc_vsync_end = p->vsync_end; 1361746fbbdbSjsg p->crtc_vtotal = p->vtotal; 1362746fbbdbSjsg 1363746fbbdbSjsg if (p->flags & DRM_MODE_FLAG_INTERLACE) { 1364746fbbdbSjsg if (adjust_flags & CRTC_INTERLACE_HALVE_V) { 1365746fbbdbSjsg p->crtc_vdisplay /= 2; 1366746fbbdbSjsg p->crtc_vsync_start /= 2; 1367746fbbdbSjsg p->crtc_vsync_end /= 2; 1368746fbbdbSjsg p->crtc_vtotal /= 2; 1369746fbbdbSjsg } 1370746fbbdbSjsg } 1371746fbbdbSjsg 13723253c27bSkettenis if (!(adjust_flags & CRTC_NO_DBLSCAN)) { 1373746fbbdbSjsg if (p->flags & DRM_MODE_FLAG_DBLSCAN) { 1374746fbbdbSjsg p->crtc_vdisplay *= 2; 1375746fbbdbSjsg p->crtc_vsync_start *= 2; 1376746fbbdbSjsg p->crtc_vsync_end *= 2; 1377746fbbdbSjsg p->crtc_vtotal *= 2; 1378746fbbdbSjsg } 13793253c27bSkettenis } 1380746fbbdbSjsg 13813253c27bSkettenis if (!(adjust_flags & CRTC_NO_VSCAN)) { 1382746fbbdbSjsg if (p->vscan > 1) { 1383746fbbdbSjsg p->crtc_vdisplay *= p->vscan; 1384746fbbdbSjsg p->crtc_vsync_start *= p->vscan; 1385746fbbdbSjsg p->crtc_vsync_end *= p->vscan; 1386746fbbdbSjsg p->crtc_vtotal *= p->vscan; 1387746fbbdbSjsg } 13883253c27bSkettenis } 1389746fbbdbSjsg 1390e1001332Skettenis if (adjust_flags & CRTC_STEREO_DOUBLE) { 1391e1001332Skettenis unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK; 1392e1001332Skettenis 1393e1001332Skettenis switch (layout) { 1394e1001332Skettenis case DRM_MODE_FLAG_3D_FRAME_PACKING: 1395e1001332Skettenis p->crtc_clock *= 2; 1396e1001332Skettenis p->crtc_vdisplay += p->crtc_vtotal; 1397e1001332Skettenis p->crtc_vsync_start += p->crtc_vtotal; 1398e1001332Skettenis p->crtc_vsync_end += p->crtc_vtotal; 1399e1001332Skettenis p->crtc_vtotal += p->crtc_vtotal; 1400e1001332Skettenis break; 1401e1001332Skettenis } 1402e1001332Skettenis } 1403e1001332Skettenis 1404746fbbdbSjsg p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); 1405746fbbdbSjsg p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); 1406746fbbdbSjsg p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); 1407746fbbdbSjsg p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); 1408746fbbdbSjsg } 1409746fbbdbSjsg EXPORT_SYMBOL(drm_mode_set_crtcinfo); 1410746fbbdbSjsg 1411746fbbdbSjsg /** 1412746fbbdbSjsg * drm_mode_copy - copy the mode 1413746fbbdbSjsg * @dst: mode to overwrite 1414746fbbdbSjsg * @src: mode to copy 1415746fbbdbSjsg * 14161bb76ff1Sjsg * Copy an existing mode into another mode, preserving the 1417e1001332Skettenis * list head of the destination mode. 1418746fbbdbSjsg */ 1419d7873f4eSjsg void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src) 1420746fbbdbSjsg { 1421e1001332Skettenis struct list_head head = dst->head; 1422746fbbdbSjsg 1423746fbbdbSjsg *dst = *src; 1424e1001332Skettenis dst->head = head; 1425746fbbdbSjsg } 1426746fbbdbSjsg EXPORT_SYMBOL(drm_mode_copy); 1427746fbbdbSjsg 1428746fbbdbSjsg /** 14291bb76ff1Sjsg * drm_mode_init - initialize the mode from another mode 14301bb76ff1Sjsg * @dst: mode to overwrite 14311bb76ff1Sjsg * @src: mode to copy 14321bb76ff1Sjsg * 14331bb76ff1Sjsg * Copy an existing mode into another mode, zeroing the 14341bb76ff1Sjsg * list head of the destination mode. Typically used 14351bb76ff1Sjsg * to guarantee the list head is not left with stack 14361bb76ff1Sjsg * garbage in on-stack modes. 14371bb76ff1Sjsg */ 14381bb76ff1Sjsg void drm_mode_init(struct drm_display_mode *dst, const struct drm_display_mode *src) 14391bb76ff1Sjsg { 14401bb76ff1Sjsg memset(dst, 0, sizeof(*dst)); 14411bb76ff1Sjsg drm_mode_copy(dst, src); 14421bb76ff1Sjsg } 14431bb76ff1Sjsg EXPORT_SYMBOL(drm_mode_init); 14441bb76ff1Sjsg 14451bb76ff1Sjsg /** 1446746fbbdbSjsg * drm_mode_duplicate - allocate and duplicate an existing mode 14473253c27bSkettenis * @dev: drm_device to allocate the duplicated mode for 14483253c27bSkettenis * @mode: mode to duplicate 1449746fbbdbSjsg * 1450746fbbdbSjsg * Just allocate a new mode, copy the existing mode into it, and return 1451746fbbdbSjsg * a pointer to it. Used to create new instances of established modes. 14523253c27bSkettenis * 14533253c27bSkettenis * Returns: 14543253c27bSkettenis * Pointer to duplicated mode on success, NULL on error. 1455746fbbdbSjsg */ 1456d7873f4eSjsg struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, 1457746fbbdbSjsg const struct drm_display_mode *mode) 1458746fbbdbSjsg { 1459746fbbdbSjsg struct drm_display_mode *nmode; 1460746fbbdbSjsg 1461746fbbdbSjsg nmode = drm_mode_create(dev); 1462746fbbdbSjsg if (!nmode) 1463746fbbdbSjsg return NULL; 1464746fbbdbSjsg 1465746fbbdbSjsg drm_mode_copy(nmode, mode); 1466746fbbdbSjsg 1467746fbbdbSjsg return nmode; 1468746fbbdbSjsg } 1469746fbbdbSjsg EXPORT_SYMBOL(drm_mode_duplicate); 1470746fbbdbSjsg 14717f4dd379Sjsg static bool drm_mode_match_timings(const struct drm_display_mode *mode1, 14727f4dd379Sjsg const struct drm_display_mode *mode2) 14737f4dd379Sjsg { 14747f4dd379Sjsg return mode1->hdisplay == mode2->hdisplay && 14757f4dd379Sjsg mode1->hsync_start == mode2->hsync_start && 14767f4dd379Sjsg mode1->hsync_end == mode2->hsync_end && 14777f4dd379Sjsg mode1->htotal == mode2->htotal && 14787f4dd379Sjsg mode1->hskew == mode2->hskew && 14797f4dd379Sjsg mode1->vdisplay == mode2->vdisplay && 14807f4dd379Sjsg mode1->vsync_start == mode2->vsync_start && 14817f4dd379Sjsg mode1->vsync_end == mode2->vsync_end && 14827f4dd379Sjsg mode1->vtotal == mode2->vtotal && 14837f4dd379Sjsg mode1->vscan == mode2->vscan; 14847f4dd379Sjsg } 14857f4dd379Sjsg 14867f4dd379Sjsg static bool drm_mode_match_clock(const struct drm_display_mode *mode1, 14877f4dd379Sjsg const struct drm_display_mode *mode2) 14887f4dd379Sjsg { 14897f4dd379Sjsg /* 14907f4dd379Sjsg * do clock check convert to PICOS 14917f4dd379Sjsg * so fb modes get matched the same 14927f4dd379Sjsg */ 14937f4dd379Sjsg if (mode1->clock && mode2->clock) 14947f4dd379Sjsg return KHZ2PICOS(mode1->clock) == KHZ2PICOS(mode2->clock); 14957f4dd379Sjsg else 14967f4dd379Sjsg return mode1->clock == mode2->clock; 14977f4dd379Sjsg } 14987f4dd379Sjsg 14997f4dd379Sjsg static bool drm_mode_match_flags(const struct drm_display_mode *mode1, 15007f4dd379Sjsg const struct drm_display_mode *mode2) 15017f4dd379Sjsg { 15027f4dd379Sjsg return (mode1->flags & ~DRM_MODE_FLAG_3D_MASK) == 15037f4dd379Sjsg (mode2->flags & ~DRM_MODE_FLAG_3D_MASK); 15047f4dd379Sjsg } 15057f4dd379Sjsg 15067f4dd379Sjsg static bool drm_mode_match_3d_flags(const struct drm_display_mode *mode1, 15077f4dd379Sjsg const struct drm_display_mode *mode2) 15087f4dd379Sjsg { 15097f4dd379Sjsg return (mode1->flags & DRM_MODE_FLAG_3D_MASK) == 15107f4dd379Sjsg (mode2->flags & DRM_MODE_FLAG_3D_MASK); 15117f4dd379Sjsg } 15127f4dd379Sjsg 15137f4dd379Sjsg static bool drm_mode_match_aspect_ratio(const struct drm_display_mode *mode1, 15147f4dd379Sjsg const struct drm_display_mode *mode2) 15157f4dd379Sjsg { 15167f4dd379Sjsg return mode1->picture_aspect_ratio == mode2->picture_aspect_ratio; 15177f4dd379Sjsg } 15187f4dd379Sjsg 15197f4dd379Sjsg /** 15207f4dd379Sjsg * drm_mode_match - test modes for (partial) equality 15217f4dd379Sjsg * @mode1: first mode 15227f4dd379Sjsg * @mode2: second mode 15237f4dd379Sjsg * @match_flags: which parts need to match (DRM_MODE_MATCH_*) 15247f4dd379Sjsg * 15257f4dd379Sjsg * Check to see if @mode1 and @mode2 are equivalent. 15267f4dd379Sjsg * 15277f4dd379Sjsg * Returns: 15287f4dd379Sjsg * True if the modes are (partially) equal, false otherwise. 15297f4dd379Sjsg */ 15307f4dd379Sjsg bool drm_mode_match(const struct drm_display_mode *mode1, 15317f4dd379Sjsg const struct drm_display_mode *mode2, 15327f4dd379Sjsg unsigned int match_flags) 15337f4dd379Sjsg { 15347f4dd379Sjsg if (!mode1 && !mode2) 15357f4dd379Sjsg return true; 15367f4dd379Sjsg 15377f4dd379Sjsg if (!mode1 || !mode2) 15387f4dd379Sjsg return false; 15397f4dd379Sjsg 15407f4dd379Sjsg if (match_flags & DRM_MODE_MATCH_TIMINGS && 15417f4dd379Sjsg !drm_mode_match_timings(mode1, mode2)) 15427f4dd379Sjsg return false; 15437f4dd379Sjsg 15447f4dd379Sjsg if (match_flags & DRM_MODE_MATCH_CLOCK && 15457f4dd379Sjsg !drm_mode_match_clock(mode1, mode2)) 15467f4dd379Sjsg return false; 15477f4dd379Sjsg 15487f4dd379Sjsg if (match_flags & DRM_MODE_MATCH_FLAGS && 15497f4dd379Sjsg !drm_mode_match_flags(mode1, mode2)) 15507f4dd379Sjsg return false; 15517f4dd379Sjsg 15527f4dd379Sjsg if (match_flags & DRM_MODE_MATCH_3D_FLAGS && 15537f4dd379Sjsg !drm_mode_match_3d_flags(mode1, mode2)) 15547f4dd379Sjsg return false; 15557f4dd379Sjsg 15567f4dd379Sjsg if (match_flags & DRM_MODE_MATCH_ASPECT_RATIO && 15577f4dd379Sjsg !drm_mode_match_aspect_ratio(mode1, mode2)) 15587f4dd379Sjsg return false; 15597f4dd379Sjsg 15607f4dd379Sjsg return true; 15617f4dd379Sjsg } 15627f4dd379Sjsg EXPORT_SYMBOL(drm_mode_match); 15637f4dd379Sjsg 1564746fbbdbSjsg /** 1565746fbbdbSjsg * drm_mode_equal - test modes for equality 1566746fbbdbSjsg * @mode1: first mode 1567746fbbdbSjsg * @mode2: second mode 1568746fbbdbSjsg * 1569746fbbdbSjsg * Check to see if @mode1 and @mode2 are equivalent. 1570746fbbdbSjsg * 15713253c27bSkettenis * Returns: 1572746fbbdbSjsg * True if the modes are equal, false otherwise. 1573746fbbdbSjsg */ 15747f4dd379Sjsg bool drm_mode_equal(const struct drm_display_mode *mode1, 15757f4dd379Sjsg const struct drm_display_mode *mode2) 1576746fbbdbSjsg { 15777f4dd379Sjsg return drm_mode_match(mode1, mode2, 15787f4dd379Sjsg DRM_MODE_MATCH_TIMINGS | 15797f4dd379Sjsg DRM_MODE_MATCH_CLOCK | 15807f4dd379Sjsg DRM_MODE_MATCH_FLAGS | 15817f4dd379Sjsg DRM_MODE_MATCH_3D_FLAGS| 15827f4dd379Sjsg DRM_MODE_MATCH_ASPECT_RATIO); 1583e1001332Skettenis } 1584e1001332Skettenis EXPORT_SYMBOL(drm_mode_equal); 1585e1001332Skettenis 1586e1001332Skettenis /** 15877f4dd379Sjsg * drm_mode_equal_no_clocks - test modes for equality 15887f4dd379Sjsg * @mode1: first mode 15897f4dd379Sjsg * @mode2: second mode 15907f4dd379Sjsg * 15917f4dd379Sjsg * Check to see if @mode1 and @mode2 are equivalent, but 15927f4dd379Sjsg * don't check the pixel clocks. 15937f4dd379Sjsg * 15947f4dd379Sjsg * Returns: 15957f4dd379Sjsg * True if the modes are equal, false otherwise. 15967f4dd379Sjsg */ 15977f4dd379Sjsg bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, 15987f4dd379Sjsg const struct drm_display_mode *mode2) 15997f4dd379Sjsg { 16007f4dd379Sjsg return drm_mode_match(mode1, mode2, 16017f4dd379Sjsg DRM_MODE_MATCH_TIMINGS | 16027f4dd379Sjsg DRM_MODE_MATCH_FLAGS | 16037f4dd379Sjsg DRM_MODE_MATCH_3D_FLAGS); 16047f4dd379Sjsg } 16057f4dd379Sjsg EXPORT_SYMBOL(drm_mode_equal_no_clocks); 16067f4dd379Sjsg 16077f4dd379Sjsg /** 1608e1001332Skettenis * drm_mode_equal_no_clocks_no_stereo - test modes for equality 1609e1001332Skettenis * @mode1: first mode 1610e1001332Skettenis * @mode2: second mode 1611e1001332Skettenis * 1612e1001332Skettenis * Check to see if @mode1 and @mode2 are equivalent, but 1613e1001332Skettenis * don't check the pixel clocks nor the stereo layout. 1614e1001332Skettenis * 16153253c27bSkettenis * Returns: 1616e1001332Skettenis * True if the modes are equal, false otherwise. 1617e1001332Skettenis */ 1618e1001332Skettenis bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, 1619e1001332Skettenis const struct drm_display_mode *mode2) 1620e1001332Skettenis { 16217f4dd379Sjsg return drm_mode_match(mode1, mode2, 16227f4dd379Sjsg DRM_MODE_MATCH_TIMINGS | 16237f4dd379Sjsg DRM_MODE_MATCH_FLAGS); 1624746fbbdbSjsg } 1625e1001332Skettenis EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); 1626746fbbdbSjsg 16277f4dd379Sjsg static enum drm_mode_status 16283253c27bSkettenis drm_mode_validate_basic(const struct drm_display_mode *mode) 16293253c27bSkettenis { 16307f4dd379Sjsg if (mode->type & ~DRM_MODE_TYPE_ALL) 16317f4dd379Sjsg return MODE_BAD; 16327f4dd379Sjsg 16337f4dd379Sjsg if (mode->flags & ~DRM_MODE_FLAG_ALL) 16347f4dd379Sjsg return MODE_BAD; 16357f4dd379Sjsg 16367f4dd379Sjsg if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) 16377f4dd379Sjsg return MODE_BAD; 16387f4dd379Sjsg 16393253c27bSkettenis if (mode->clock == 0) 16403253c27bSkettenis return MODE_CLOCK_LOW; 16413253c27bSkettenis 16423253c27bSkettenis if (mode->hdisplay == 0 || 16433253c27bSkettenis mode->hsync_start < mode->hdisplay || 16443253c27bSkettenis mode->hsync_end < mode->hsync_start || 16453253c27bSkettenis mode->htotal < mode->hsync_end) 16463253c27bSkettenis return MODE_H_ILLEGAL; 16473253c27bSkettenis 16483253c27bSkettenis if (mode->vdisplay == 0 || 16493253c27bSkettenis mode->vsync_start < mode->vdisplay || 16503253c27bSkettenis mode->vsync_end < mode->vsync_start || 16513253c27bSkettenis mode->vtotal < mode->vsync_end) 16523253c27bSkettenis return MODE_V_ILLEGAL; 16533253c27bSkettenis 16543253c27bSkettenis return MODE_OK; 16553253c27bSkettenis } 16567f4dd379Sjsg 16577f4dd379Sjsg /** 16587f4dd379Sjsg * drm_mode_validate_driver - make sure the mode is somewhat sane 16597f4dd379Sjsg * @dev: drm device 16607f4dd379Sjsg * @mode: mode to check 16617f4dd379Sjsg * 16627f4dd379Sjsg * First do basic validation on the mode, and then allow the driver 16637f4dd379Sjsg * to check for device/driver specific limitations via the optional 16647f4dd379Sjsg * &drm_mode_config_helper_funcs.mode_valid hook. 16657f4dd379Sjsg * 16667f4dd379Sjsg * Returns: 16677f4dd379Sjsg * The mode status 16687f4dd379Sjsg */ 16697f4dd379Sjsg enum drm_mode_status 16707f4dd379Sjsg drm_mode_validate_driver(struct drm_device *dev, 16717f4dd379Sjsg const struct drm_display_mode *mode) 16727f4dd379Sjsg { 16737f4dd379Sjsg enum drm_mode_status status; 16747f4dd379Sjsg 16757f4dd379Sjsg status = drm_mode_validate_basic(mode); 16767f4dd379Sjsg if (status != MODE_OK) 16777f4dd379Sjsg return status; 16787f4dd379Sjsg 16797f4dd379Sjsg if (dev->mode_config.funcs->mode_valid) 16807f4dd379Sjsg return dev->mode_config.funcs->mode_valid(dev, mode); 16817f4dd379Sjsg else 16827f4dd379Sjsg return MODE_OK; 16837f4dd379Sjsg } 16847f4dd379Sjsg EXPORT_SYMBOL(drm_mode_validate_driver); 16853253c27bSkettenis 16863253c27bSkettenis /** 1687746fbbdbSjsg * drm_mode_validate_size - make sure modes adhere to size constraints 16883253c27bSkettenis * @mode: mode to check 1689746fbbdbSjsg * @maxX: maximum width 1690746fbbdbSjsg * @maxY: maximum height 1691746fbbdbSjsg * 16923253c27bSkettenis * This function is a helper which can be used to validate modes against size 16933253c27bSkettenis * limitations of the DRM device/connector. If a mode is too big its status 16943253c27bSkettenis * member is updated with the appropriate validation failure code. The list 16953253c27bSkettenis * itself is not changed. 1696746fbbdbSjsg * 16973253c27bSkettenis * Returns: 16983253c27bSkettenis * The mode status 1699746fbbdbSjsg */ 17003253c27bSkettenis enum drm_mode_status 17013253c27bSkettenis drm_mode_validate_size(const struct drm_display_mode *mode, 17023253c27bSkettenis int maxX, int maxY) 1703746fbbdbSjsg { 1704746fbbdbSjsg if (maxX > 0 && mode->hdisplay > maxX) 17053253c27bSkettenis return MODE_VIRTUAL_X; 1706746fbbdbSjsg 1707746fbbdbSjsg if (maxY > 0 && mode->vdisplay > maxY) 17083253c27bSkettenis return MODE_VIRTUAL_Y; 17093253c27bSkettenis 17103253c27bSkettenis return MODE_OK; 1711746fbbdbSjsg } 1712746fbbdbSjsg EXPORT_SYMBOL(drm_mode_validate_size); 1713746fbbdbSjsg 17147f4dd379Sjsg /** 17157f4dd379Sjsg * drm_mode_validate_ycbcr420 - add 'ycbcr420-only' modes only when allowed 17167f4dd379Sjsg * @mode: mode to check 17177f4dd379Sjsg * @connector: drm connector under action 17187f4dd379Sjsg * 17197f4dd379Sjsg * This function is a helper which can be used to filter out any YCBCR420 17207f4dd379Sjsg * only mode, when the source doesn't support it. 17217f4dd379Sjsg * 17227f4dd379Sjsg * Returns: 17237f4dd379Sjsg * The mode status 17247f4dd379Sjsg */ 17257f4dd379Sjsg enum drm_mode_status 17267f4dd379Sjsg drm_mode_validate_ycbcr420(const struct drm_display_mode *mode, 17277f4dd379Sjsg struct drm_connector *connector) 17287f4dd379Sjsg { 17295ca02815Sjsg if (!connector->ycbcr_420_allowed && 17305ca02815Sjsg drm_mode_is_420_only(&connector->display_info, mode)) 17315ca02815Sjsg return MODE_NO_420; 17327f4dd379Sjsg 17335ca02815Sjsg return MODE_OK; 17347f4dd379Sjsg } 17357f4dd379Sjsg EXPORT_SYMBOL(drm_mode_validate_ycbcr420); 17363253c27bSkettenis 17373253c27bSkettenis #define MODE_STATUS(status) [MODE_ ## status + 3] = #status 17383253c27bSkettenis 17393253c27bSkettenis static const char * const drm_mode_status_names[] = { 17403253c27bSkettenis MODE_STATUS(OK), 17413253c27bSkettenis MODE_STATUS(HSYNC), 17423253c27bSkettenis MODE_STATUS(VSYNC), 17433253c27bSkettenis MODE_STATUS(H_ILLEGAL), 17443253c27bSkettenis MODE_STATUS(V_ILLEGAL), 17453253c27bSkettenis MODE_STATUS(BAD_WIDTH), 17463253c27bSkettenis MODE_STATUS(NOMODE), 17473253c27bSkettenis MODE_STATUS(NO_INTERLACE), 17483253c27bSkettenis MODE_STATUS(NO_DBLESCAN), 17493253c27bSkettenis MODE_STATUS(NO_VSCAN), 17503253c27bSkettenis MODE_STATUS(MEM), 17513253c27bSkettenis MODE_STATUS(VIRTUAL_X), 17523253c27bSkettenis MODE_STATUS(VIRTUAL_Y), 17533253c27bSkettenis MODE_STATUS(MEM_VIRT), 17543253c27bSkettenis MODE_STATUS(NOCLOCK), 17553253c27bSkettenis MODE_STATUS(CLOCK_HIGH), 17563253c27bSkettenis MODE_STATUS(CLOCK_LOW), 17573253c27bSkettenis MODE_STATUS(CLOCK_RANGE), 17583253c27bSkettenis MODE_STATUS(BAD_HVALUE), 17593253c27bSkettenis MODE_STATUS(BAD_VVALUE), 17603253c27bSkettenis MODE_STATUS(BAD_VSCAN), 17613253c27bSkettenis MODE_STATUS(HSYNC_NARROW), 17623253c27bSkettenis MODE_STATUS(HSYNC_WIDE), 17633253c27bSkettenis MODE_STATUS(HBLANK_NARROW), 17643253c27bSkettenis MODE_STATUS(HBLANK_WIDE), 17653253c27bSkettenis MODE_STATUS(VSYNC_NARROW), 17663253c27bSkettenis MODE_STATUS(VSYNC_WIDE), 17673253c27bSkettenis MODE_STATUS(VBLANK_NARROW), 17683253c27bSkettenis MODE_STATUS(VBLANK_WIDE), 17693253c27bSkettenis MODE_STATUS(PANEL), 17703253c27bSkettenis MODE_STATUS(INTERLACE_WIDTH), 17713253c27bSkettenis MODE_STATUS(ONE_WIDTH), 17723253c27bSkettenis MODE_STATUS(ONE_HEIGHT), 17733253c27bSkettenis MODE_STATUS(ONE_SIZE), 17743253c27bSkettenis MODE_STATUS(NO_REDUCED), 17753253c27bSkettenis MODE_STATUS(NO_STEREO), 17767f4dd379Sjsg MODE_STATUS(NO_420), 17777f4dd379Sjsg MODE_STATUS(STALE), 17783253c27bSkettenis MODE_STATUS(BAD), 17793253c27bSkettenis MODE_STATUS(ERROR), 17803253c27bSkettenis }; 17813253c27bSkettenis 17823253c27bSkettenis #undef MODE_STATUS 17833253c27bSkettenis 17847f4dd379Sjsg const char *drm_get_mode_status_name(enum drm_mode_status status) 17853253c27bSkettenis { 17863253c27bSkettenis int index = status + 3; 17873253c27bSkettenis 17883253c27bSkettenis if (WARN_ON(index < 0 || index >= ARRAY_SIZE(drm_mode_status_names))) 17893253c27bSkettenis return ""; 17903253c27bSkettenis 17913253c27bSkettenis return drm_mode_status_names[index]; 17923253c27bSkettenis } 17933253c27bSkettenis 1794746fbbdbSjsg /** 1795746fbbdbSjsg * drm_mode_prune_invalid - remove invalid modes from mode list 1796746fbbdbSjsg * @dev: DRM device 1797746fbbdbSjsg * @mode_list: list of modes to check 1798746fbbdbSjsg * @verbose: be verbose about it 1799746fbbdbSjsg * 18003253c27bSkettenis * This helper function can be used to prune a display mode list after 1801c349dbc7Sjsg * validation has been completed. All modes whose status is not MODE_OK will be 18023253c27bSkettenis * removed from the list, and if @verbose the status code and mode name is also 18033253c27bSkettenis * printed to dmesg. 1804746fbbdbSjsg */ 1805d7873f4eSjsg void drm_mode_prune_invalid(struct drm_device *dev, 1806746fbbdbSjsg struct list_head *mode_list, bool verbose) 1807746fbbdbSjsg { 1808746fbbdbSjsg struct drm_display_mode *mode, *t; 1809746fbbdbSjsg 1810746fbbdbSjsg list_for_each_entry_safe(mode, t, mode_list, head) { 1811746fbbdbSjsg if (mode->status != MODE_OK) { 1812746fbbdbSjsg list_del(&mode->head); 18131bb76ff1Sjsg if (mode->type & DRM_MODE_TYPE_USERDEF) { 18141bb76ff1Sjsg drm_warn(dev, "User-defined mode not supported: " 18151bb76ff1Sjsg DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); 18161bb76ff1Sjsg } 1817746fbbdbSjsg if (verbose) { 1818746fbbdbSjsg drm_mode_debug_printmodeline(mode); 18193253c27bSkettenis DRM_DEBUG_KMS("Not using %s mode: %s\n", 18203253c27bSkettenis mode->name, 18213253c27bSkettenis drm_get_mode_status_name(mode->status)); 1822746fbbdbSjsg } 1823746fbbdbSjsg drm_mode_destroy(dev, mode); 1824746fbbdbSjsg } 1825746fbbdbSjsg } 1826746fbbdbSjsg } 1827746fbbdbSjsg EXPORT_SYMBOL(drm_mode_prune_invalid); 1828746fbbdbSjsg 1829746fbbdbSjsg /** 1830746fbbdbSjsg * drm_mode_compare - compare modes for favorability 1831746fbbdbSjsg * @priv: unused 1832746fbbdbSjsg * @lh_a: list_head for first mode 1833746fbbdbSjsg * @lh_b: list_head for second mode 1834746fbbdbSjsg * 1835746fbbdbSjsg * Compare two modes, given by @lh_a and @lh_b, returning a value indicating 1836746fbbdbSjsg * which is better. 1837746fbbdbSjsg * 18383253c27bSkettenis * Returns: 1839746fbbdbSjsg * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or 1840746fbbdbSjsg * positive if @lh_b is better than @lh_a. 1841746fbbdbSjsg */ 18426dafb210Sjsg static int drm_mode_compare(void *priv, const struct list_head *lh_a, 18436dafb210Sjsg const struct list_head *lh_b) 1844746fbbdbSjsg { 18453253c27bSkettenis struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); 18463253c27bSkettenis struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); 1847746fbbdbSjsg int diff; 1848746fbbdbSjsg 1849746fbbdbSjsg diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - 1850746fbbdbSjsg ((a->type & DRM_MODE_TYPE_PREFERRED) != 0); 1851746fbbdbSjsg if (diff) 1852746fbbdbSjsg return diff; 1853746fbbdbSjsg diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; 1854746fbbdbSjsg if (diff) 1855746fbbdbSjsg return diff; 1856e1001332Skettenis 1857ad8b1aafSjsg diff = drm_mode_vrefresh(b) - drm_mode_vrefresh(a); 1858e1001332Skettenis if (diff) 1859e1001332Skettenis return diff; 1860e1001332Skettenis 1861746fbbdbSjsg diff = b->clock - a->clock; 1862746fbbdbSjsg return diff; 1863746fbbdbSjsg } 1864746fbbdbSjsg 1865746fbbdbSjsg /** 1866746fbbdbSjsg * drm_mode_sort - sort mode list 18673253c27bSkettenis * @mode_list: list of drm_display_mode structures to sort 1868746fbbdbSjsg * 18693253c27bSkettenis * Sort @mode_list by favorability, moving good modes to the head of the list. 1870746fbbdbSjsg */ 1871d7873f4eSjsg void drm_mode_sort(struct list_head *mode_list) 1872746fbbdbSjsg { 18733253c27bSkettenis list_sort(NULL, mode_list, drm_mode_compare); 1874746fbbdbSjsg } 1875746fbbdbSjsg EXPORT_SYMBOL(drm_mode_sort); 1876746fbbdbSjsg 1877746fbbdbSjsg /** 18787f4dd379Sjsg * drm_connector_list_update - update the mode list for the connector 1879746fbbdbSjsg * @connector: the connector to update 1880746fbbdbSjsg * 1881746fbbdbSjsg * This moves the modes from the @connector probed_modes list 1882746fbbdbSjsg * to the actual mode list. It compares the probed mode against the current 18833253c27bSkettenis * list and only adds different/new modes. 18843253c27bSkettenis * 18853253c27bSkettenis * This is just a helper functions doesn't validate any modes itself and also 18863253c27bSkettenis * doesn't prune any invalid modes. Callers need to do that themselves. 1887746fbbdbSjsg */ 18887f4dd379Sjsg void drm_connector_list_update(struct drm_connector *connector) 1889746fbbdbSjsg { 1890746fbbdbSjsg struct drm_display_mode *pmode, *pt; 1891746fbbdbSjsg 18923253c27bSkettenis WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); 18933253c27bSkettenis 18947f4dd379Sjsg list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) { 18957f4dd379Sjsg struct drm_display_mode *mode; 18967f4dd379Sjsg bool found_it = false; 18977f4dd379Sjsg 1898746fbbdbSjsg /* go through current modes checking for the new probed mode */ 1899746fbbdbSjsg list_for_each_entry(mode, &connector->modes, head) { 19007f4dd379Sjsg if (!drm_mode_equal(pmode, mode)) 19017f4dd379Sjsg continue; 19027f4dd379Sjsg 19037f4dd379Sjsg found_it = true; 19047f4dd379Sjsg 19057f4dd379Sjsg /* 19067f4dd379Sjsg * If the old matching mode is stale (ie. left over 19077f4dd379Sjsg * from a previous probe) just replace it outright. 19087f4dd379Sjsg * Otherwise just merge the type bits between all 19097f4dd379Sjsg * equal probed modes. 19107f4dd379Sjsg * 19117f4dd379Sjsg * If two probed modes are considered equal, pick the 19127f4dd379Sjsg * actual timings from the one that's marked as 19137f4dd379Sjsg * preferred (in case the match isn't 100%). If 19147f4dd379Sjsg * multiple or zero preferred modes are present, favor 19157f4dd379Sjsg * the mode added to the probed_modes list first. 19167f4dd379Sjsg */ 19177f4dd379Sjsg if (mode->status == MODE_STALE) { 19187f4dd379Sjsg drm_mode_copy(mode, pmode); 19197f4dd379Sjsg } else if ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 && 19207f4dd379Sjsg (pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) { 19217f4dd379Sjsg pmode->type |= mode->type; 19227f4dd379Sjsg drm_mode_copy(mode, pmode); 19237f4dd379Sjsg } else { 1924746fbbdbSjsg mode->type |= pmode->type; 19257f4dd379Sjsg } 19267f4dd379Sjsg 1927746fbbdbSjsg list_del(&pmode->head); 1928746fbbdbSjsg drm_mode_destroy(connector->dev, pmode); 1929746fbbdbSjsg break; 1930746fbbdbSjsg } 1931746fbbdbSjsg 1932746fbbdbSjsg if (!found_it) { 1933746fbbdbSjsg list_move_tail(&pmode->head, &connector->modes); 1934746fbbdbSjsg } 1935746fbbdbSjsg } 1936746fbbdbSjsg } 19377f4dd379Sjsg EXPORT_SYMBOL(drm_connector_list_update); 1938746fbbdbSjsg 1939c349dbc7Sjsg #ifdef __linux__ 1940c349dbc7Sjsg 1941c349dbc7Sjsg static int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr, 1942c349dbc7Sjsg struct drm_cmdline_mode *mode) 1943c349dbc7Sjsg { 1944c349dbc7Sjsg unsigned int bpp; 1945c349dbc7Sjsg 1946c349dbc7Sjsg if (str[0] != '-') 1947c349dbc7Sjsg return -EINVAL; 1948c349dbc7Sjsg 1949c349dbc7Sjsg str++; 1950c349dbc7Sjsg bpp = simple_strtol(str, end_ptr, 10); 1951c349dbc7Sjsg if (*end_ptr == str) 1952c349dbc7Sjsg return -EINVAL; 1953c349dbc7Sjsg 1954c349dbc7Sjsg mode->bpp = bpp; 1955c349dbc7Sjsg mode->bpp_specified = true; 1956c349dbc7Sjsg 1957c349dbc7Sjsg return 0; 1958c349dbc7Sjsg } 1959c349dbc7Sjsg 1960c349dbc7Sjsg static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr, 1961c349dbc7Sjsg struct drm_cmdline_mode *mode) 1962c349dbc7Sjsg { 1963c349dbc7Sjsg unsigned int refresh; 1964c349dbc7Sjsg 1965c349dbc7Sjsg if (str[0] != '@') 1966c349dbc7Sjsg return -EINVAL; 1967c349dbc7Sjsg 1968c349dbc7Sjsg str++; 1969c349dbc7Sjsg refresh = simple_strtol(str, end_ptr, 10); 1970c349dbc7Sjsg if (*end_ptr == str) 1971c349dbc7Sjsg return -EINVAL; 1972c349dbc7Sjsg 1973c349dbc7Sjsg mode->refresh = refresh; 1974c349dbc7Sjsg mode->refresh_specified = true; 1975c349dbc7Sjsg 1976c349dbc7Sjsg return 0; 1977c349dbc7Sjsg } 1978c349dbc7Sjsg 1979c349dbc7Sjsg static int drm_mode_parse_cmdline_extra(const char *str, int length, 1980c349dbc7Sjsg bool freestanding, 1981c349dbc7Sjsg const struct drm_connector *connector, 1982c349dbc7Sjsg struct drm_cmdline_mode *mode) 1983c349dbc7Sjsg { 1984c349dbc7Sjsg int i; 1985c349dbc7Sjsg 1986c349dbc7Sjsg for (i = 0; i < length; i++) { 1987c349dbc7Sjsg switch (str[i]) { 1988c349dbc7Sjsg case 'i': 1989c349dbc7Sjsg if (freestanding) 1990c349dbc7Sjsg return -EINVAL; 1991c349dbc7Sjsg 1992c349dbc7Sjsg mode->interlace = true; 1993c349dbc7Sjsg break; 1994c349dbc7Sjsg case 'm': 1995c349dbc7Sjsg if (freestanding) 1996c349dbc7Sjsg return -EINVAL; 1997c349dbc7Sjsg 1998c349dbc7Sjsg mode->margins = true; 1999c349dbc7Sjsg break; 2000c349dbc7Sjsg case 'D': 2001c349dbc7Sjsg if (mode->force != DRM_FORCE_UNSPECIFIED) 2002c349dbc7Sjsg return -EINVAL; 2003c349dbc7Sjsg 2004c349dbc7Sjsg if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && 2005c349dbc7Sjsg (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) 2006c349dbc7Sjsg mode->force = DRM_FORCE_ON; 2007c349dbc7Sjsg else 2008c349dbc7Sjsg mode->force = DRM_FORCE_ON_DIGITAL; 2009c349dbc7Sjsg break; 2010c349dbc7Sjsg case 'd': 2011c349dbc7Sjsg if (mode->force != DRM_FORCE_UNSPECIFIED) 2012c349dbc7Sjsg return -EINVAL; 2013c349dbc7Sjsg 2014c349dbc7Sjsg mode->force = DRM_FORCE_OFF; 2015c349dbc7Sjsg break; 2016c349dbc7Sjsg case 'e': 2017c349dbc7Sjsg if (mode->force != DRM_FORCE_UNSPECIFIED) 2018c349dbc7Sjsg return -EINVAL; 2019c349dbc7Sjsg 2020c349dbc7Sjsg mode->force = DRM_FORCE_ON; 2021c349dbc7Sjsg break; 2022c349dbc7Sjsg default: 2023c349dbc7Sjsg return -EINVAL; 2024c349dbc7Sjsg } 2025c349dbc7Sjsg } 2026c349dbc7Sjsg 2027c349dbc7Sjsg return 0; 2028c349dbc7Sjsg } 2029c349dbc7Sjsg 2030c349dbc7Sjsg static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, 2031c349dbc7Sjsg bool extras, 2032c349dbc7Sjsg const struct drm_connector *connector, 2033c349dbc7Sjsg struct drm_cmdline_mode *mode) 2034c349dbc7Sjsg { 2035c349dbc7Sjsg const char *str_start = str; 2036c349dbc7Sjsg bool rb = false, cvt = false; 2037c349dbc7Sjsg int xres = 0, yres = 0; 2038c349dbc7Sjsg int remaining, i; 2039c349dbc7Sjsg char *end_ptr; 2040c349dbc7Sjsg 2041c349dbc7Sjsg xres = simple_strtol(str, &end_ptr, 10); 2042c349dbc7Sjsg if (end_ptr == str) 2043c349dbc7Sjsg return -EINVAL; 2044c349dbc7Sjsg 2045c349dbc7Sjsg if (end_ptr[0] != 'x') 2046c349dbc7Sjsg return -EINVAL; 2047c349dbc7Sjsg end_ptr++; 2048c349dbc7Sjsg 2049c349dbc7Sjsg str = end_ptr; 2050c349dbc7Sjsg yres = simple_strtol(str, &end_ptr, 10); 2051c349dbc7Sjsg if (end_ptr == str) 2052c349dbc7Sjsg return -EINVAL; 2053c349dbc7Sjsg 2054c349dbc7Sjsg remaining = length - (end_ptr - str_start); 2055c349dbc7Sjsg if (remaining < 0) 2056c349dbc7Sjsg return -EINVAL; 2057c349dbc7Sjsg 2058c349dbc7Sjsg for (i = 0; i < remaining; i++) { 2059c349dbc7Sjsg switch (end_ptr[i]) { 2060c349dbc7Sjsg case 'M': 2061c349dbc7Sjsg cvt = true; 2062c349dbc7Sjsg break; 2063c349dbc7Sjsg case 'R': 2064c349dbc7Sjsg rb = true; 2065c349dbc7Sjsg break; 2066c349dbc7Sjsg default: 2067c349dbc7Sjsg /* 2068c349dbc7Sjsg * Try to pass that to our extras parsing 2069c349dbc7Sjsg * function to handle the case where the 2070c349dbc7Sjsg * extras are directly after the resolution 2071c349dbc7Sjsg */ 2072c349dbc7Sjsg if (extras) { 2073c349dbc7Sjsg int ret = drm_mode_parse_cmdline_extra(end_ptr + i, 2074c349dbc7Sjsg 1, 2075c349dbc7Sjsg false, 2076c349dbc7Sjsg connector, 2077c349dbc7Sjsg mode); 2078c349dbc7Sjsg if (ret) 2079c349dbc7Sjsg return ret; 2080c349dbc7Sjsg } else { 2081c349dbc7Sjsg return -EINVAL; 2082c349dbc7Sjsg } 2083c349dbc7Sjsg } 2084c349dbc7Sjsg } 2085c349dbc7Sjsg 2086c349dbc7Sjsg mode->xres = xres; 2087c349dbc7Sjsg mode->yres = yres; 2088c349dbc7Sjsg mode->cvt = cvt; 2089c349dbc7Sjsg mode->rb = rb; 2090c349dbc7Sjsg 2091c349dbc7Sjsg return 0; 2092c349dbc7Sjsg } 2093c349dbc7Sjsg 2094c349dbc7Sjsg static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret) 2095c349dbc7Sjsg { 2096c349dbc7Sjsg const char *value; 2097c349dbc7Sjsg char *endp; 2098c349dbc7Sjsg 2099c349dbc7Sjsg /* 2100c349dbc7Sjsg * delim must point to the '=', otherwise it is a syntax error and 21015ca02815Sjsg * if delim points to the terminating zero, then delim + 1 will point 2102c349dbc7Sjsg * past the end of the string. 2103c349dbc7Sjsg */ 2104c349dbc7Sjsg if (*delim != '=') 2105c349dbc7Sjsg return -EINVAL; 2106c349dbc7Sjsg 2107c349dbc7Sjsg value = delim + 1; 2108c349dbc7Sjsg *int_ret = simple_strtol(value, &endp, 10); 2109c349dbc7Sjsg 2110c349dbc7Sjsg /* Make sure we have parsed something */ 2111c349dbc7Sjsg if (endp == value) 2112c349dbc7Sjsg return -EINVAL; 2113c349dbc7Sjsg 2114c349dbc7Sjsg return 0; 2115c349dbc7Sjsg } 2116c349dbc7Sjsg 2117c349dbc7Sjsg static int drm_mode_parse_panel_orientation(const char *delim, 2118c349dbc7Sjsg struct drm_cmdline_mode *mode) 2119c349dbc7Sjsg { 2120c349dbc7Sjsg const char *value; 2121c349dbc7Sjsg 2122c349dbc7Sjsg if (*delim != '=') 2123c349dbc7Sjsg return -EINVAL; 2124c349dbc7Sjsg 2125c349dbc7Sjsg value = delim + 1; 2126c349dbc7Sjsg delim = strchr(value, ','); 2127c349dbc7Sjsg if (!delim) 2128c349dbc7Sjsg delim = value + strlen(value); 2129c349dbc7Sjsg 2130c349dbc7Sjsg if (!strncmp(value, "normal", delim - value)) 2131c349dbc7Sjsg mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; 2132c349dbc7Sjsg else if (!strncmp(value, "upside_down", delim - value)) 2133c349dbc7Sjsg mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; 2134c349dbc7Sjsg else if (!strncmp(value, "left_side_up", delim - value)) 2135c349dbc7Sjsg mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; 2136c349dbc7Sjsg else if (!strncmp(value, "right_side_up", delim - value)) 2137c349dbc7Sjsg mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; 2138c349dbc7Sjsg else 2139c349dbc7Sjsg return -EINVAL; 2140c349dbc7Sjsg 2141c349dbc7Sjsg return 0; 2142c349dbc7Sjsg } 2143c349dbc7Sjsg 2144f005ef32Sjsg static int drm_mode_parse_tv_mode(const char *delim, 2145f005ef32Sjsg struct drm_cmdline_mode *mode) 2146f005ef32Sjsg { 2147f005ef32Sjsg const char *value; 2148f005ef32Sjsg int ret; 2149f005ef32Sjsg 2150f005ef32Sjsg if (*delim != '=') 2151f005ef32Sjsg return -EINVAL; 2152f005ef32Sjsg 2153f005ef32Sjsg value = delim + 1; 2154f005ef32Sjsg delim = strchr(value, ','); 2155f005ef32Sjsg if (!delim) 2156f005ef32Sjsg delim = value + strlen(value); 2157f005ef32Sjsg 2158f005ef32Sjsg ret = drm_get_tv_mode_from_name(value, delim - value); 2159f005ef32Sjsg if (ret < 0) 2160f005ef32Sjsg return ret; 2161f005ef32Sjsg 2162f005ef32Sjsg mode->tv_mode_specified = true; 2163f005ef32Sjsg mode->tv_mode = ret; 2164f005ef32Sjsg 2165f005ef32Sjsg return 0; 2166f005ef32Sjsg } 2167f005ef32Sjsg 2168c349dbc7Sjsg static int drm_mode_parse_cmdline_options(const char *str, 2169c349dbc7Sjsg bool freestanding, 2170c349dbc7Sjsg const struct drm_connector *connector, 2171c349dbc7Sjsg struct drm_cmdline_mode *mode) 2172c349dbc7Sjsg { 2173c349dbc7Sjsg unsigned int deg, margin, rotation = 0; 2174c349dbc7Sjsg const char *delim, *option, *sep; 2175c349dbc7Sjsg 2176c349dbc7Sjsg option = str; 2177c349dbc7Sjsg do { 2178c349dbc7Sjsg delim = strchr(option, '='); 2179c349dbc7Sjsg if (!delim) { 2180c349dbc7Sjsg delim = strchr(option, ','); 2181c349dbc7Sjsg 2182c349dbc7Sjsg if (!delim) 2183c349dbc7Sjsg delim = option + strlen(option); 2184c349dbc7Sjsg } 2185c349dbc7Sjsg 2186c349dbc7Sjsg if (!strncmp(option, "rotate", delim - option)) { 2187c349dbc7Sjsg if (drm_mode_parse_cmdline_int(delim, °)) 2188c349dbc7Sjsg return -EINVAL; 2189c349dbc7Sjsg 2190c349dbc7Sjsg switch (deg) { 2191c349dbc7Sjsg case 0: 2192c349dbc7Sjsg rotation |= DRM_MODE_ROTATE_0; 2193c349dbc7Sjsg break; 2194c349dbc7Sjsg 2195c349dbc7Sjsg case 90: 2196c349dbc7Sjsg rotation |= DRM_MODE_ROTATE_90; 2197c349dbc7Sjsg break; 2198c349dbc7Sjsg 2199c349dbc7Sjsg case 180: 2200c349dbc7Sjsg rotation |= DRM_MODE_ROTATE_180; 2201c349dbc7Sjsg break; 2202c349dbc7Sjsg 2203c349dbc7Sjsg case 270: 2204c349dbc7Sjsg rotation |= DRM_MODE_ROTATE_270; 2205c349dbc7Sjsg break; 2206c349dbc7Sjsg 2207c349dbc7Sjsg default: 2208c349dbc7Sjsg return -EINVAL; 2209c349dbc7Sjsg } 2210c349dbc7Sjsg } else if (!strncmp(option, "reflect_x", delim - option)) { 2211c349dbc7Sjsg rotation |= DRM_MODE_REFLECT_X; 2212c349dbc7Sjsg } else if (!strncmp(option, "reflect_y", delim - option)) { 2213c349dbc7Sjsg rotation |= DRM_MODE_REFLECT_Y; 2214c349dbc7Sjsg } else if (!strncmp(option, "margin_right", delim - option)) { 2215c349dbc7Sjsg if (drm_mode_parse_cmdline_int(delim, &margin)) 2216c349dbc7Sjsg return -EINVAL; 2217c349dbc7Sjsg 2218c349dbc7Sjsg mode->tv_margins.right = margin; 2219c349dbc7Sjsg } else if (!strncmp(option, "margin_left", delim - option)) { 2220c349dbc7Sjsg if (drm_mode_parse_cmdline_int(delim, &margin)) 2221c349dbc7Sjsg return -EINVAL; 2222c349dbc7Sjsg 2223c349dbc7Sjsg mode->tv_margins.left = margin; 2224c349dbc7Sjsg } else if (!strncmp(option, "margin_top", delim - option)) { 2225c349dbc7Sjsg if (drm_mode_parse_cmdline_int(delim, &margin)) 2226c349dbc7Sjsg return -EINVAL; 2227c349dbc7Sjsg 2228c349dbc7Sjsg mode->tv_margins.top = margin; 2229c349dbc7Sjsg } else if (!strncmp(option, "margin_bottom", delim - option)) { 2230c349dbc7Sjsg if (drm_mode_parse_cmdline_int(delim, &margin)) 2231c349dbc7Sjsg return -EINVAL; 2232c349dbc7Sjsg 2233c349dbc7Sjsg mode->tv_margins.bottom = margin; 2234c349dbc7Sjsg } else if (!strncmp(option, "panel_orientation", delim - option)) { 2235c349dbc7Sjsg if (drm_mode_parse_panel_orientation(delim, mode)) 2236c349dbc7Sjsg return -EINVAL; 2237f005ef32Sjsg } else if (!strncmp(option, "tv_mode", delim - option)) { 2238f005ef32Sjsg if (drm_mode_parse_tv_mode(delim, mode)) 2239f005ef32Sjsg return -EINVAL; 2240c349dbc7Sjsg } else { 2241c349dbc7Sjsg return -EINVAL; 2242c349dbc7Sjsg } 2243c349dbc7Sjsg sep = strchr(delim, ','); 2244c349dbc7Sjsg option = sep + 1; 2245c349dbc7Sjsg } while (sep); 2246c349dbc7Sjsg 2247c349dbc7Sjsg if (rotation && freestanding) 2248c349dbc7Sjsg return -EINVAL; 2249c349dbc7Sjsg 2250c349dbc7Sjsg if (!(rotation & DRM_MODE_ROTATE_MASK)) 2251c349dbc7Sjsg rotation |= DRM_MODE_ROTATE_0; 2252c349dbc7Sjsg 2253c349dbc7Sjsg /* Make sure there is exactly one rotation defined */ 2254c349dbc7Sjsg if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)) 2255c349dbc7Sjsg return -EINVAL; 2256c349dbc7Sjsg 2257c349dbc7Sjsg mode->rotation_reflection = rotation; 2258c349dbc7Sjsg 2259c349dbc7Sjsg return 0; 2260c349dbc7Sjsg } 2261c349dbc7Sjsg 2262c349dbc7Sjsg #endif /* __linux__ */ 2263c349dbc7Sjsg 2264f005ef32Sjsg struct drm_named_mode { 2265f005ef32Sjsg const char *name; 2266f005ef32Sjsg unsigned int pixel_clock_khz; 2267f005ef32Sjsg unsigned int xres; 2268f005ef32Sjsg unsigned int yres; 2269f005ef32Sjsg unsigned int flags; 2270f005ef32Sjsg unsigned int tv_mode; 2271c349dbc7Sjsg }; 2272c349dbc7Sjsg 2273f005ef32Sjsg #define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \ 2274f005ef32Sjsg { \ 2275f005ef32Sjsg .name = _name, \ 2276f005ef32Sjsg .pixel_clock_khz = _pclk, \ 2277f005ef32Sjsg .xres = _x, \ 2278f005ef32Sjsg .yres = _y, \ 2279f005ef32Sjsg .flags = _flags, \ 2280f005ef32Sjsg .tv_mode = _mode, \ 2281f005ef32Sjsg } 2282f005ef32Sjsg 2283f005ef32Sjsg static const struct drm_named_mode drm_named_modes[] = { 2284f005ef32Sjsg NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC), 2285f005ef32Sjsg NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J), 2286f005ef32Sjsg NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL), 2287f005ef32Sjsg NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M), 2288f005ef32Sjsg }; 2289f005ef32Sjsg 2290f005ef32Sjsg #ifdef __linux__ 2291f005ef32Sjsg 2292f005ef32Sjsg static int drm_mode_parse_cmdline_named_mode(const char *name, 2293f005ef32Sjsg unsigned int name_end, 2294f005ef32Sjsg struct drm_cmdline_mode *cmdline_mode) 2295f005ef32Sjsg { 2296f005ef32Sjsg unsigned int i; 2297f005ef32Sjsg 2298f005ef32Sjsg if (!name_end) 2299f005ef32Sjsg return 0; 2300f005ef32Sjsg 2301f005ef32Sjsg /* If the name starts with a digit, it's not a named mode */ 2302f005ef32Sjsg if (isdigit(name[0])) 2303f005ef32Sjsg return 0; 2304f005ef32Sjsg 2305f005ef32Sjsg /* 2306f005ef32Sjsg * If there's an equal sign in the name, the command-line 2307f005ef32Sjsg * contains only an option and no mode. 2308f005ef32Sjsg */ 2309f005ef32Sjsg if (strnchr(name, name_end, '=')) 2310f005ef32Sjsg return 0; 2311f005ef32Sjsg 2312f005ef32Sjsg /* The connection status extras can be set without a mode. */ 2313f005ef32Sjsg if (name_end == 1 && 2314f005ef32Sjsg (name[0] == 'd' || name[0] == 'D' || name[0] == 'e')) 2315f005ef32Sjsg return 0; 2316f005ef32Sjsg 2317f005ef32Sjsg /* 2318f005ef32Sjsg * We're sure we're a named mode at this point, iterate over the 2319f005ef32Sjsg * list of modes we're aware of. 2320f005ef32Sjsg */ 2321f005ef32Sjsg for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { 2322f005ef32Sjsg const struct drm_named_mode *mode = &drm_named_modes[i]; 2323f005ef32Sjsg int ret; 2324f005ef32Sjsg 2325f005ef32Sjsg ret = str_has_prefix(name, mode->name); 2326f005ef32Sjsg if (ret != name_end) 2327f005ef32Sjsg continue; 2328f005ef32Sjsg 2329f005ef32Sjsg strscpy(cmdline_mode->name, mode->name, sizeof(cmdline_mode->name)); 2330f005ef32Sjsg cmdline_mode->pixel_clock = mode->pixel_clock_khz; 2331f005ef32Sjsg cmdline_mode->xres = mode->xres; 2332f005ef32Sjsg cmdline_mode->yres = mode->yres; 2333f005ef32Sjsg cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 2334f005ef32Sjsg cmdline_mode->tv_mode = mode->tv_mode; 2335f005ef32Sjsg cmdline_mode->tv_mode_specified = true; 2336f005ef32Sjsg cmdline_mode->specified = true; 2337f005ef32Sjsg 2338f005ef32Sjsg return 1; 2339f005ef32Sjsg } 2340f005ef32Sjsg 2341f005ef32Sjsg return -EINVAL; 2342f005ef32Sjsg } 2343f005ef32Sjsg 2344f005ef32Sjsg #endif /* __linux__ */ 2345f005ef32Sjsg 2346746fbbdbSjsg /** 23473253c27bSkettenis * drm_mode_parse_command_line_for_connector - parse command line modeline for connector 23483253c27bSkettenis * @mode_option: optional per connector mode option 23493253c27bSkettenis * @connector: connector to parse modeline for 23503253c27bSkettenis * @mode: preallocated drm_cmdline_mode structure to fill out 2351746fbbdbSjsg * 23523253c27bSkettenis * This parses @mode_option command line modeline for modes and options to 2353f005ef32Sjsg * configure the connector. 2354746fbbdbSjsg * 23553253c27bSkettenis * This uses the same parameters as the fb modedb.c, except for an extra 23567f4dd379Sjsg * force-enable, force-enable-digital and force-disable bit at the end:: 23573253c27bSkettenis * 2358746fbbdbSjsg * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] 2359746fbbdbSjsg * 2360c349dbc7Sjsg * Additionals options can be provided following the mode, using a comma to 2361c349dbc7Sjsg * separate each option. Valid options can be found in 2362c349dbc7Sjsg * Documentation/fb/modedb.rst. 2363c349dbc7Sjsg * 23643253c27bSkettenis * The intermediate drm_cmdline_mode structure is required to store additional 23653253c27bSkettenis * options from the command line modline like the force-enable/disable flag. 23663253c27bSkettenis * 23673253c27bSkettenis * Returns: 23683253c27bSkettenis * True if a valid modeline has been parsed, false otherwise. 2369746fbbdbSjsg */ 2370d7873f4eSjsg bool drm_mode_parse_command_line_for_connector(const char *mode_option, 2371c349dbc7Sjsg const struct drm_connector *connector, 2372746fbbdbSjsg struct drm_cmdline_mode *mode) 2373746fbbdbSjsg { 23743253c27bSkettenis #ifdef __linux__ 2375746fbbdbSjsg const char *name; 2376c349dbc7Sjsg bool freestanding = false, parse_extras = false; 2377c349dbc7Sjsg unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; 2378c349dbc7Sjsg unsigned int mode_end = 0; 2379c349dbc7Sjsg const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; 2380c349dbc7Sjsg const char *options_ptr = NULL; 2381c349dbc7Sjsg char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; 2382f005ef32Sjsg int len, ret; 2383746fbbdbSjsg 2384c349dbc7Sjsg memset(mode, 0, sizeof(*mode)); 2385c349dbc7Sjsg mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 2386c349dbc7Sjsg 2387746fbbdbSjsg if (!mode_option) 2388746fbbdbSjsg return false; 2389746fbbdbSjsg 2390746fbbdbSjsg name = mode_option; 2391746fbbdbSjsg 2392c349dbc7Sjsg /* Locate the start of named options */ 2393c349dbc7Sjsg options_ptr = strchr(name, ','); 2394c349dbc7Sjsg if (options_ptr) 2395c349dbc7Sjsg options_off = options_ptr - name; 2396f005ef32Sjsg else 2397f005ef32Sjsg options_off = strlen(name); 2398f005ef32Sjsg 2399f005ef32Sjsg /* Try to locate the bpp and refresh specifiers, if any */ 2400f005ef32Sjsg bpp_ptr = strnchr(name, options_off, '-'); 2401f005ef32Sjsg while (bpp_ptr && !isdigit(bpp_ptr[1])) 2402f005ef32Sjsg bpp_ptr = strnchr(bpp_ptr + 1, options_off, '-'); 2403f005ef32Sjsg if (bpp_ptr) 2404f005ef32Sjsg bpp_off = bpp_ptr - name; 2405f005ef32Sjsg 2406f005ef32Sjsg refresh_ptr = strnchr(name, options_off, '@'); 2407f005ef32Sjsg if (refresh_ptr) 2408f005ef32Sjsg refresh_off = refresh_ptr - name; 2409c349dbc7Sjsg 2410c349dbc7Sjsg /* Locate the end of the name / resolution, and parse it */ 2411c349dbc7Sjsg if (bpp_ptr) { 2412c349dbc7Sjsg mode_end = bpp_off; 2413c349dbc7Sjsg } else if (refresh_ptr) { 2414c349dbc7Sjsg mode_end = refresh_off; 2415c349dbc7Sjsg } else if (options_ptr) { 2416c349dbc7Sjsg mode_end = options_off; 2417c349dbc7Sjsg parse_extras = true; 2418c349dbc7Sjsg } else { 2419c349dbc7Sjsg mode_end = strlen(name); 2420c349dbc7Sjsg parse_extras = true; 2421c349dbc7Sjsg } 2422c349dbc7Sjsg 2423f005ef32Sjsg if (!mode_end) 2424f005ef32Sjsg return false; 2425c349dbc7Sjsg 2426f005ef32Sjsg ret = drm_mode_parse_cmdline_named_mode(name, mode_end, mode); 2427f005ef32Sjsg if (ret < 0) 2428f005ef32Sjsg return false; 2429f005ef32Sjsg 2430f005ef32Sjsg /* 2431f005ef32Sjsg * Having a mode that starts by a letter (and thus is named) and 2432f005ef32Sjsg * an at-sign (used to specify a refresh rate) is disallowed. 2433f005ef32Sjsg */ 2434f005ef32Sjsg if (ret && refresh_ptr) 2435f005ef32Sjsg return false; 2436c349dbc7Sjsg 2437c349dbc7Sjsg /* No named mode? Check for a normal mode argument, e.g. 1024x768 */ 2438c349dbc7Sjsg if (!mode->specified && isdigit(name[0])) { 2439c349dbc7Sjsg ret = drm_mode_parse_cmdline_res_mode(name, mode_end, 2440c349dbc7Sjsg parse_extras, 2441c349dbc7Sjsg connector, 2442c349dbc7Sjsg mode); 2443c349dbc7Sjsg if (ret) 2444c349dbc7Sjsg return false; 2445c349dbc7Sjsg 2446c349dbc7Sjsg mode->specified = true; 2447c349dbc7Sjsg } 2448c349dbc7Sjsg 2449c349dbc7Sjsg /* No mode? Check for freestanding extras and/or options */ 2450c349dbc7Sjsg if (!mode->specified) { 2451c349dbc7Sjsg unsigned int len = strlen(mode_option); 2452c349dbc7Sjsg 2453c349dbc7Sjsg if (bpp_ptr || refresh_ptr) 2454c349dbc7Sjsg return false; /* syntax error */ 2455c349dbc7Sjsg 2456c349dbc7Sjsg if (len == 1 || (len >= 2 && mode_option[1] == ',')) 2457c349dbc7Sjsg extra_ptr = mode_option; 2458746fbbdbSjsg else 2459c349dbc7Sjsg options_ptr = mode_option - 1; 2460746fbbdbSjsg 2461c349dbc7Sjsg freestanding = true; 2462746fbbdbSjsg } 2463746fbbdbSjsg 2464c349dbc7Sjsg if (bpp_ptr) { 2465c349dbc7Sjsg ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode); 2466c349dbc7Sjsg if (ret) 2467c349dbc7Sjsg return false; 2468c349dbc7Sjsg 2469c349dbc7Sjsg mode->bpp_specified = true; 2470c349dbc7Sjsg } 2471c349dbc7Sjsg 2472c349dbc7Sjsg if (refresh_ptr) { 2473c349dbc7Sjsg ret = drm_mode_parse_cmdline_refresh(refresh_ptr, 2474c349dbc7Sjsg &refresh_end_ptr, mode); 2475c349dbc7Sjsg if (ret) 2476c349dbc7Sjsg return false; 2477c349dbc7Sjsg 2478c349dbc7Sjsg mode->refresh_specified = true; 2479c349dbc7Sjsg } 2480c349dbc7Sjsg 2481c349dbc7Sjsg /* 2482c349dbc7Sjsg * Locate the end of the bpp / refresh, and parse the extras 2483c349dbc7Sjsg * if relevant 2484c349dbc7Sjsg */ 2485c349dbc7Sjsg if (bpp_ptr && refresh_ptr) 2486c349dbc7Sjsg extra_ptr = max(bpp_end_ptr, refresh_end_ptr); 2487c349dbc7Sjsg else if (bpp_ptr) 2488c349dbc7Sjsg extra_ptr = bpp_end_ptr; 2489c349dbc7Sjsg else if (refresh_ptr) 2490c349dbc7Sjsg extra_ptr = refresh_end_ptr; 2491c349dbc7Sjsg 2492c349dbc7Sjsg if (extra_ptr) { 2493c349dbc7Sjsg if (options_ptr) 2494c349dbc7Sjsg len = options_ptr - extra_ptr; 2495746fbbdbSjsg else 2496c349dbc7Sjsg len = strlen(extra_ptr); 2497c349dbc7Sjsg 2498c349dbc7Sjsg ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding, 2499c349dbc7Sjsg connector, mode); 2500c349dbc7Sjsg if (ret) 2501746fbbdbSjsg return false; 2502746fbbdbSjsg } 2503746fbbdbSjsg 2504c349dbc7Sjsg if (options_ptr) { 2505c349dbc7Sjsg ret = drm_mode_parse_cmdline_options(options_ptr + 1, 2506c349dbc7Sjsg freestanding, 2507c349dbc7Sjsg connector, mode); 2508c349dbc7Sjsg if (ret) 2509c349dbc7Sjsg return false; 2510746fbbdbSjsg } 2511746fbbdbSjsg 2512746fbbdbSjsg return true; 25133253c27bSkettenis #else 25143253c27bSkettenis return false; 25153253c27bSkettenis #endif 2516746fbbdbSjsg } 2517746fbbdbSjsg EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); 2518746fbbdbSjsg 2519f005ef32Sjsg static struct drm_display_mode *drm_named_mode(struct drm_device *dev, 2520f005ef32Sjsg struct drm_cmdline_mode *cmd) 2521f005ef32Sjsg { 2522f005ef32Sjsg unsigned int i; 2523f005ef32Sjsg 2524f005ef32Sjsg for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { 2525f005ef32Sjsg const struct drm_named_mode *named_mode = &drm_named_modes[i]; 2526f005ef32Sjsg 2527f005ef32Sjsg if (strcmp(cmd->name, named_mode->name)) 2528f005ef32Sjsg continue; 2529f005ef32Sjsg 2530f005ef32Sjsg if (!cmd->tv_mode_specified) 2531f005ef32Sjsg continue; 2532f005ef32Sjsg 2533f005ef32Sjsg return drm_analog_tv_mode(dev, 2534f005ef32Sjsg named_mode->tv_mode, 2535f005ef32Sjsg named_mode->pixel_clock_khz * 1000, 2536f005ef32Sjsg named_mode->xres, 2537f005ef32Sjsg named_mode->yres, 2538f005ef32Sjsg named_mode->flags & DRM_MODE_FLAG_INTERLACE); 2539f005ef32Sjsg } 2540f005ef32Sjsg 2541f005ef32Sjsg return NULL; 2542f005ef32Sjsg } 2543f005ef32Sjsg 25443253c27bSkettenis /** 25453253c27bSkettenis * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode 25463253c27bSkettenis * @dev: DRM device to create the new mode for 25473253c27bSkettenis * @cmd: input command line modeline 25483253c27bSkettenis * 25493253c27bSkettenis * Returns: 25503253c27bSkettenis * Pointer to converted mode on success, NULL on error. 25513253c27bSkettenis */ 2552746fbbdbSjsg struct drm_display_mode * 2553746fbbdbSjsg drm_mode_create_from_cmdline_mode(struct drm_device *dev, 2554746fbbdbSjsg struct drm_cmdline_mode *cmd) 2555746fbbdbSjsg { 2556746fbbdbSjsg struct drm_display_mode *mode; 2557746fbbdbSjsg 25585ca02815Sjsg if (cmd->xres == 0 || cmd->yres == 0) 25595ca02815Sjsg return NULL; 25605ca02815Sjsg 2561f005ef32Sjsg if (strlen(cmd->name)) 2562f005ef32Sjsg mode = drm_named_mode(dev, cmd); 2563f005ef32Sjsg else if (cmd->cvt) 2564746fbbdbSjsg mode = drm_cvt_mode(dev, 2565746fbbdbSjsg cmd->xres, cmd->yres, 2566746fbbdbSjsg cmd->refresh_specified ? cmd->refresh : 60, 2567746fbbdbSjsg cmd->rb, cmd->interlace, 2568746fbbdbSjsg cmd->margins); 2569746fbbdbSjsg else 2570746fbbdbSjsg mode = drm_gtf_mode(dev, 2571746fbbdbSjsg cmd->xres, cmd->yres, 2572746fbbdbSjsg cmd->refresh_specified ? cmd->refresh : 60, 2573746fbbdbSjsg cmd->interlace, 2574746fbbdbSjsg cmd->margins); 2575746fbbdbSjsg if (!mode) 2576746fbbdbSjsg return NULL; 2577746fbbdbSjsg 25783253c27bSkettenis mode->type |= DRM_MODE_TYPE_USERDEF; 25793253c27bSkettenis /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ 25807f4dd379Sjsg if (cmd->xres == 1366) 25817f4dd379Sjsg drm_mode_fixup_1366x768(mode); 2582746fbbdbSjsg drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 2583746fbbdbSjsg return mode; 2584746fbbdbSjsg } 2585746fbbdbSjsg EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); 2586746fbbdbSjsg 25873253c27bSkettenis /** 25885ca02815Sjsg * drm_mode_convert_to_umode - convert a drm_display_mode into a modeinfo 25893253c27bSkettenis * @out: drm_mode_modeinfo struct to return to the user 25903253c27bSkettenis * @in: drm_display_mode to use 2591746fbbdbSjsg * 25923253c27bSkettenis * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 25933253c27bSkettenis * the user. 2594746fbbdbSjsg */ 25953253c27bSkettenis void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, 25963253c27bSkettenis const struct drm_display_mode *in) 2597746fbbdbSjsg { 25983253c27bSkettenis out->clock = in->clock; 25993253c27bSkettenis out->hdisplay = in->hdisplay; 26003253c27bSkettenis out->hsync_start = in->hsync_start; 26013253c27bSkettenis out->hsync_end = in->hsync_end; 26023253c27bSkettenis out->htotal = in->htotal; 26033253c27bSkettenis out->hskew = in->hskew; 26043253c27bSkettenis out->vdisplay = in->vdisplay; 26053253c27bSkettenis out->vsync_start = in->vsync_start; 26063253c27bSkettenis out->vsync_end = in->vsync_end; 26073253c27bSkettenis out->vtotal = in->vtotal; 26083253c27bSkettenis out->vscan = in->vscan; 2609ad8b1aafSjsg out->vrefresh = drm_mode_vrefresh(in); 26103253c27bSkettenis out->flags = in->flags; 26113253c27bSkettenis out->type = in->type; 26127f4dd379Sjsg 26137f4dd379Sjsg switch (in->picture_aspect_ratio) { 26147f4dd379Sjsg case HDMI_PICTURE_ASPECT_4_3: 26157f4dd379Sjsg out->flags |= DRM_MODE_FLAG_PIC_AR_4_3; 26167f4dd379Sjsg break; 26177f4dd379Sjsg case HDMI_PICTURE_ASPECT_16_9: 26187f4dd379Sjsg out->flags |= DRM_MODE_FLAG_PIC_AR_16_9; 26197f4dd379Sjsg break; 26207f4dd379Sjsg case HDMI_PICTURE_ASPECT_64_27: 26217f4dd379Sjsg out->flags |= DRM_MODE_FLAG_PIC_AR_64_27; 26227f4dd379Sjsg break; 26237f4dd379Sjsg case HDMI_PICTURE_ASPECT_256_135: 26247f4dd379Sjsg out->flags |= DRM_MODE_FLAG_PIC_AR_256_135; 26257f4dd379Sjsg break; 26267f4dd379Sjsg default: 2627c349dbc7Sjsg WARN(1, "Invalid aspect ratio (0%x) on mode\n", 2628c349dbc7Sjsg in->picture_aspect_ratio); 2629ad8b1aafSjsg fallthrough; 2630c349dbc7Sjsg case HDMI_PICTURE_ASPECT_NONE: 26317f4dd379Sjsg out->flags |= DRM_MODE_FLAG_PIC_AR_NONE; 26327f4dd379Sjsg break; 26337f4dd379Sjsg } 26347f4dd379Sjsg 26353253c27bSkettenis strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 26363253c27bSkettenis out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 2637746fbbdbSjsg } 2638746fbbdbSjsg 26393253c27bSkettenis /** 26405ca02815Sjsg * drm_mode_convert_umode - convert a modeinfo into a drm_display_mode 26417f4dd379Sjsg * @dev: drm device 26423253c27bSkettenis * @out: drm_display_mode to return to the user 26433253c27bSkettenis * @in: drm_mode_modeinfo to use 2644746fbbdbSjsg * 26453253c27bSkettenis * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 26463253c27bSkettenis * the caller. 26473253c27bSkettenis * 26483253c27bSkettenis * Returns: 26493253c27bSkettenis * Zero on success, negative errno on failure. 2650746fbbdbSjsg */ 26517f4dd379Sjsg int drm_mode_convert_umode(struct drm_device *dev, 26527f4dd379Sjsg struct drm_display_mode *out, 26533253c27bSkettenis const struct drm_mode_modeinfo *in) 26543253c27bSkettenis { 26557f4dd379Sjsg if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 26567f4dd379Sjsg return -ERANGE; 26573253c27bSkettenis 26583253c27bSkettenis out->clock = in->clock; 26593253c27bSkettenis out->hdisplay = in->hdisplay; 26603253c27bSkettenis out->hsync_start = in->hsync_start; 26613253c27bSkettenis out->hsync_end = in->hsync_end; 26623253c27bSkettenis out->htotal = in->htotal; 26633253c27bSkettenis out->hskew = in->hskew; 26643253c27bSkettenis out->vdisplay = in->vdisplay; 26653253c27bSkettenis out->vsync_start = in->vsync_start; 26663253c27bSkettenis out->vsync_end = in->vsync_end; 26673253c27bSkettenis out->vtotal = in->vtotal; 26683253c27bSkettenis out->vscan = in->vscan; 26693253c27bSkettenis out->flags = in->flags; 26707f4dd379Sjsg /* 26717f4dd379Sjsg * Old xf86-video-vmware (possibly others too) used to 26725ca02815Sjsg * leave 'type' uninitialized. Just ignore any bits we 26737f4dd379Sjsg * don't like. It's a just hint after all, and more 26747f4dd379Sjsg * useful for the kernel->userspace direction anyway. 26757f4dd379Sjsg */ 26767f4dd379Sjsg out->type = in->type & DRM_MODE_TYPE_ALL; 26773253c27bSkettenis strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 26783253c27bSkettenis out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 26793253c27bSkettenis 26807f4dd379Sjsg /* Clearing picture aspect ratio bits from out flags, 26817f4dd379Sjsg * as the aspect-ratio information is not stored in 26827f4dd379Sjsg * flags for kernel-mode, but in picture_aspect_ratio. 26837f4dd379Sjsg */ 26847f4dd379Sjsg out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; 26857f4dd379Sjsg 26867f4dd379Sjsg switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) { 26877f4dd379Sjsg case DRM_MODE_FLAG_PIC_AR_4_3: 2688c349dbc7Sjsg out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3; 26897f4dd379Sjsg break; 26907f4dd379Sjsg case DRM_MODE_FLAG_PIC_AR_16_9: 2691c349dbc7Sjsg out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; 26927f4dd379Sjsg break; 26937f4dd379Sjsg case DRM_MODE_FLAG_PIC_AR_64_27: 2694c349dbc7Sjsg out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27; 26957f4dd379Sjsg break; 26967f4dd379Sjsg case DRM_MODE_FLAG_PIC_AR_256_135: 2697c349dbc7Sjsg out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; 26987f4dd379Sjsg break; 2699c349dbc7Sjsg case DRM_MODE_FLAG_PIC_AR_NONE: 27007f4dd379Sjsg out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; 27017f4dd379Sjsg break; 2702c349dbc7Sjsg default: 2703c349dbc7Sjsg return -EINVAL; 27047f4dd379Sjsg } 27057f4dd379Sjsg 27067f4dd379Sjsg out->status = drm_mode_validate_driver(dev, out); 27073253c27bSkettenis if (out->status != MODE_OK) 27087f4dd379Sjsg return -EINVAL; 27093253c27bSkettenis 27103253c27bSkettenis drm_mode_set_crtcinfo(out, CRTC_INTERLACE_HALVE_V); 27113253c27bSkettenis 27127f4dd379Sjsg return 0; 2713746fbbdbSjsg } 27147f4dd379Sjsg 27157f4dd379Sjsg /** 27167f4dd379Sjsg * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 27177f4dd379Sjsg * output format 27187f4dd379Sjsg * 27197f4dd379Sjsg * @display: display under action 27207f4dd379Sjsg * @mode: video mode to be tested. 27217f4dd379Sjsg * 27227f4dd379Sjsg * Returns: 27237f4dd379Sjsg * true if the mode can be supported in YCBCR420 format 27247f4dd379Sjsg * false if not. 27257f4dd379Sjsg */ 27267f4dd379Sjsg bool drm_mode_is_420_only(const struct drm_display_info *display, 27277f4dd379Sjsg const struct drm_display_mode *mode) 27287f4dd379Sjsg { 27297f4dd379Sjsg u8 vic = drm_match_cea_mode(mode); 27307f4dd379Sjsg 27317f4dd379Sjsg return test_bit(vic, display->hdmi.y420_vdb_modes); 27327f4dd379Sjsg } 27337f4dd379Sjsg EXPORT_SYMBOL(drm_mode_is_420_only); 27347f4dd379Sjsg 27357f4dd379Sjsg /** 27367f4dd379Sjsg * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 27377f4dd379Sjsg * output format also (along with RGB/YCBCR444/422) 27387f4dd379Sjsg * 27397f4dd379Sjsg * @display: display under action. 27407f4dd379Sjsg * @mode: video mode to be tested. 27417f4dd379Sjsg * 27427f4dd379Sjsg * Returns: 27437f4dd379Sjsg * true if the mode can be support YCBCR420 format 27447f4dd379Sjsg * false if not. 27457f4dd379Sjsg */ 27467f4dd379Sjsg bool drm_mode_is_420_also(const struct drm_display_info *display, 27477f4dd379Sjsg const struct drm_display_mode *mode) 27487f4dd379Sjsg { 27497f4dd379Sjsg u8 vic = drm_match_cea_mode(mode); 27507f4dd379Sjsg 27517f4dd379Sjsg return test_bit(vic, display->hdmi.y420_cmdb_modes); 27527f4dd379Sjsg } 27537f4dd379Sjsg EXPORT_SYMBOL(drm_mode_is_420_also); 27547f4dd379Sjsg /** 27557f4dd379Sjsg * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 27567f4dd379Sjsg * output format 27577f4dd379Sjsg * 27587f4dd379Sjsg * @display: display under action. 27597f4dd379Sjsg * @mode: video mode to be tested. 27607f4dd379Sjsg * 27617f4dd379Sjsg * Returns: 27627f4dd379Sjsg * true if the mode can be supported in YCBCR420 format 27637f4dd379Sjsg * false if not. 27647f4dd379Sjsg */ 27657f4dd379Sjsg bool drm_mode_is_420(const struct drm_display_info *display, 27667f4dd379Sjsg const struct drm_display_mode *mode) 27677f4dd379Sjsg { 27687f4dd379Sjsg return drm_mode_is_420_only(display, mode) || 27697f4dd379Sjsg drm_mode_is_420_also(display, mode); 27707f4dd379Sjsg } 27717f4dd379Sjsg EXPORT_SYMBOL(drm_mode_is_420); 2772