18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 1997-2003 by The XFree86 Project, Inc. 38c2ecf20Sopenharmony_ci * Copyright © 2007 Dave Airlie 48c2ecf20Sopenharmony_ci * Copyright © 2007-2008 Intel Corporation 58c2ecf20Sopenharmony_ci * Jesse Barnes <jesse.barnes@intel.com> 68c2ecf20Sopenharmony_ci * Copyright 2005-2006 Luc Verhaegen 78c2ecf20Sopenharmony_ci * Copyright (c) 2001, Andy Ritger aritger@nvidia.com 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 108c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 118c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 128c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 138c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 148c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 178c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 208c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 218c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 228c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 238c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 248c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 258c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Except as contained in this notice, the name of the copyright holder(s) 288c2ecf20Sopenharmony_ci * and author(s) shall not be used in advertising or otherwise to promote 298c2ecf20Sopenharmony_ci * the sale, use or other dealings in this Software without prior written 308c2ecf20Sopenharmony_ci * authorization from the copyright holder(s) and author(s). 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/ctype.h> 348c2ecf20Sopenharmony_ci#include <linux/list.h> 358c2ecf20Sopenharmony_ci#include <linux/list_sort.h> 368c2ecf20Sopenharmony_ci#include <linux/export.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 398c2ecf20Sopenharmony_ci#include <video/videomode.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 428c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 438c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 448c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include "drm_crtc_internal.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/** 498c2ecf20Sopenharmony_ci * drm_mode_debug_printmodeline - print a mode to dmesg 508c2ecf20Sopenharmony_ci * @mode: mode to print 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * Describe @mode using DRM_DEBUG. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_civoid drm_mode_debug_printmodeline(const struct drm_display_mode *mode) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_debug_printmodeline); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * drm_mode_create - create a new display mode 628c2ecf20Sopenharmony_ci * @dev: DRM device 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it 658c2ecf20Sopenharmony_ci * and return it. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Returns: 688c2ecf20Sopenharmony_ci * Pointer to new mode on success, NULL on error. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistruct drm_display_mode *drm_mode_create(struct drm_device *dev) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct drm_display_mode *nmode; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); 758c2ecf20Sopenharmony_ci if (!nmode) 768c2ecf20Sopenharmony_ci return NULL; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return nmode; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_create); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * drm_mode_destroy - remove a mode 848c2ecf20Sopenharmony_ci * @dev: DRM device 858c2ecf20Sopenharmony_ci * @mode: mode to remove 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Release @mode's unique ID, then free it @mode structure itself using kfree. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_civoid drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (!mode) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci kfree(mode); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_destroy); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/** 998c2ecf20Sopenharmony_ci * drm_mode_probed_add - add a mode to a connector's probed_mode list 1008c2ecf20Sopenharmony_ci * @connector: connector the new mode 1018c2ecf20Sopenharmony_ci * @mode: mode data 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * Add @mode to @connector's probed_mode list for later use. This list should 1048c2ecf20Sopenharmony_ci * then in a second step get filtered and all the modes actually supported by 1058c2ecf20Sopenharmony_ci * the hardware moved to the @connector's modes list. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_civoid drm_mode_probed_add(struct drm_connector *connector, 1088c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci list_add_tail(&mode->head, &connector->probed_modes); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_probed_add); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/** 1178c2ecf20Sopenharmony_ci * drm_cvt_mode -create a modeline based on the CVT algorithm 1188c2ecf20Sopenharmony_ci * @dev: drm device 1198c2ecf20Sopenharmony_ci * @hdisplay: hdisplay size 1208c2ecf20Sopenharmony_ci * @vdisplay: vdisplay size 1218c2ecf20Sopenharmony_ci * @vrefresh: vrefresh rate 1228c2ecf20Sopenharmony_ci * @reduced: whether to use reduced blanking 1238c2ecf20Sopenharmony_ci * @interlaced: whether to compute an interlaced mode 1248c2ecf20Sopenharmony_ci * @margins: whether to add margins (borders) 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * This function is called to generate the modeline based on CVT algorithm 1278c2ecf20Sopenharmony_ci * according to the hdisplay, vdisplay, vrefresh. 1288c2ecf20Sopenharmony_ci * It is based from the VESA(TM) Coordinated Video Timing Generator by 1298c2ecf20Sopenharmony_ci * Graham Loveridge April 9, 2003 available at 1308c2ecf20Sopenharmony_ci * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. 1338c2ecf20Sopenharmony_ci * What I have done is to translate it by using integer calculation. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * Returns: 1368c2ecf20Sopenharmony_ci * The modeline based on the CVT algorithm stored in a drm_display_mode object. 1378c2ecf20Sopenharmony_ci * The display mode object is allocated with drm_mode_create(). Returns NULL 1388c2ecf20Sopenharmony_ci * when no mode could be allocated. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistruct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, 1418c2ecf20Sopenharmony_ci int vdisplay, int vrefresh, 1428c2ecf20Sopenharmony_ci bool reduced, bool interlaced, bool margins) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci#define HV_FACTOR 1000 1458c2ecf20Sopenharmony_ci /* 1) top/bottom margin size (% of height) - default: 1.8, */ 1468c2ecf20Sopenharmony_ci#define CVT_MARGIN_PERCENTAGE 18 1478c2ecf20Sopenharmony_ci /* 2) character cell horizontal granularity (pixels) - default 8 */ 1488c2ecf20Sopenharmony_ci#define CVT_H_GRANULARITY 8 1498c2ecf20Sopenharmony_ci /* 3) Minimum vertical porch (lines) - default 3 */ 1508c2ecf20Sopenharmony_ci#define CVT_MIN_V_PORCH 3 1518c2ecf20Sopenharmony_ci /* 4) Minimum number of vertical back porch lines - default 6 */ 1528c2ecf20Sopenharmony_ci#define CVT_MIN_V_BPORCH 6 1538c2ecf20Sopenharmony_ci /* Pixel Clock step (kHz) */ 1548c2ecf20Sopenharmony_ci#define CVT_CLOCK_STEP 250 1558c2ecf20Sopenharmony_ci struct drm_display_mode *drm_mode; 1568c2ecf20Sopenharmony_ci unsigned int vfieldrate, hperiod; 1578c2ecf20Sopenharmony_ci int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; 1588c2ecf20Sopenharmony_ci int interlace; 1598c2ecf20Sopenharmony_ci u64 tmp; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!hdisplay || !vdisplay) 1628c2ecf20Sopenharmony_ci return NULL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* allocate the drm_display_mode structure. If failure, we will 1658c2ecf20Sopenharmony_ci * return directly 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci drm_mode = drm_mode_create(dev); 1688c2ecf20Sopenharmony_ci if (!drm_mode) 1698c2ecf20Sopenharmony_ci return NULL; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* the CVT default refresh rate is 60Hz */ 1728c2ecf20Sopenharmony_ci if (!vrefresh) 1738c2ecf20Sopenharmony_ci vrefresh = 60; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* the required field fresh rate */ 1768c2ecf20Sopenharmony_ci if (interlaced) 1778c2ecf20Sopenharmony_ci vfieldrate = vrefresh * 2; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci vfieldrate = vrefresh; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* horizontal pixels */ 1828c2ecf20Sopenharmony_ci hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* determine the left&right borders */ 1858c2ecf20Sopenharmony_ci hmargin = 0; 1868c2ecf20Sopenharmony_ci if (margins) { 1878c2ecf20Sopenharmony_ci hmargin = hdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; 1888c2ecf20Sopenharmony_ci hmargin -= hmargin % CVT_H_GRANULARITY; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci /* find the total active pixels */ 1918c2ecf20Sopenharmony_ci drm_mode->hdisplay = hdisplay_rnd + 2 * hmargin; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* find the number of lines per field */ 1948c2ecf20Sopenharmony_ci if (interlaced) 1958c2ecf20Sopenharmony_ci vdisplay_rnd = vdisplay / 2; 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci vdisplay_rnd = vdisplay; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* find the top & bottom borders */ 2008c2ecf20Sopenharmony_ci vmargin = 0; 2018c2ecf20Sopenharmony_ci if (margins) 2028c2ecf20Sopenharmony_ci vmargin = vdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci drm_mode->vdisplay = vdisplay + 2 * vmargin; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Interlaced */ 2078c2ecf20Sopenharmony_ci if (interlaced) 2088c2ecf20Sopenharmony_ci interlace = 1; 2098c2ecf20Sopenharmony_ci else 2108c2ecf20Sopenharmony_ci interlace = 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Determine VSync Width from aspect ratio */ 2138c2ecf20Sopenharmony_ci if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) 2148c2ecf20Sopenharmony_ci vsync = 4; 2158c2ecf20Sopenharmony_ci else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) 2168c2ecf20Sopenharmony_ci vsync = 5; 2178c2ecf20Sopenharmony_ci else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) 2188c2ecf20Sopenharmony_ci vsync = 6; 2198c2ecf20Sopenharmony_ci else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) 2208c2ecf20Sopenharmony_ci vsync = 7; 2218c2ecf20Sopenharmony_ci else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) 2228c2ecf20Sopenharmony_ci vsync = 7; 2238c2ecf20Sopenharmony_ci else /* custom */ 2248c2ecf20Sopenharmony_ci vsync = 10; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!reduced) { 2278c2ecf20Sopenharmony_ci /* simplify the GTF calculation */ 2288c2ecf20Sopenharmony_ci /* 4) Minimum time of vertical sync + back porch interval (µs) 2298c2ecf20Sopenharmony_ci * default 550.0 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci int tmp1, tmp2; 2328c2ecf20Sopenharmony_ci#define CVT_MIN_VSYNC_BP 550 2338c2ecf20Sopenharmony_ci /* 3) Nominal HSync width (% of line period) - default 8 */ 2348c2ecf20Sopenharmony_ci#define CVT_HSYNC_PERCENTAGE 8 2358c2ecf20Sopenharmony_ci unsigned int hblank_percentage; 2368c2ecf20Sopenharmony_ci int vsyncandback_porch, __maybe_unused vback_porch, hblank; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* estimated the horizontal period */ 2398c2ecf20Sopenharmony_ci tmp1 = HV_FACTOR * 1000000 - 2408c2ecf20Sopenharmony_ci CVT_MIN_VSYNC_BP * HV_FACTOR * vfieldrate; 2418c2ecf20Sopenharmony_ci tmp2 = (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH) * 2 + 2428c2ecf20Sopenharmony_ci interlace; 2438c2ecf20Sopenharmony_ci hperiod = tmp1 * 2 / (tmp2 * vfieldrate); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / hperiod + 1; 2468c2ecf20Sopenharmony_ci /* 9. Find number of lines in sync + backporch */ 2478c2ecf20Sopenharmony_ci if (tmp1 < (vsync + CVT_MIN_V_PORCH)) 2488c2ecf20Sopenharmony_ci vsyncandback_porch = vsync + CVT_MIN_V_PORCH; 2498c2ecf20Sopenharmony_ci else 2508c2ecf20Sopenharmony_ci vsyncandback_porch = tmp1; 2518c2ecf20Sopenharmony_ci /* 10. Find number of lines in back porch */ 2528c2ecf20Sopenharmony_ci vback_porch = vsyncandback_porch - vsync; 2538c2ecf20Sopenharmony_ci drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + 2548c2ecf20Sopenharmony_ci vsyncandback_porch + CVT_MIN_V_PORCH; 2558c2ecf20Sopenharmony_ci /* 5) Definition of Horizontal blanking time limitation */ 2568c2ecf20Sopenharmony_ci /* Gradient (%/kHz) - default 600 */ 2578c2ecf20Sopenharmony_ci#define CVT_M_FACTOR 600 2588c2ecf20Sopenharmony_ci /* Offset (%) - default 40 */ 2598c2ecf20Sopenharmony_ci#define CVT_C_FACTOR 40 2608c2ecf20Sopenharmony_ci /* Blanking time scaling factor - default 128 */ 2618c2ecf20Sopenharmony_ci#define CVT_K_FACTOR 128 2628c2ecf20Sopenharmony_ci /* Scaling factor weighting - default 20 */ 2638c2ecf20Sopenharmony_ci#define CVT_J_FACTOR 20 2648c2ecf20Sopenharmony_ci#define CVT_M_PRIME (CVT_M_FACTOR * CVT_K_FACTOR / 256) 2658c2ecf20Sopenharmony_ci#define CVT_C_PRIME ((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ 2668c2ecf20Sopenharmony_ci CVT_J_FACTOR) 2678c2ecf20Sopenharmony_ci /* 12. Find ideal blanking duty cycle from formula */ 2688c2ecf20Sopenharmony_ci hblank_percentage = CVT_C_PRIME * HV_FACTOR - CVT_M_PRIME * 2698c2ecf20Sopenharmony_ci hperiod / 1000; 2708c2ecf20Sopenharmony_ci /* 13. Blanking time */ 2718c2ecf20Sopenharmony_ci if (hblank_percentage < 20 * HV_FACTOR) 2728c2ecf20Sopenharmony_ci hblank_percentage = 20 * HV_FACTOR; 2738c2ecf20Sopenharmony_ci hblank = drm_mode->hdisplay * hblank_percentage / 2748c2ecf20Sopenharmony_ci (100 * HV_FACTOR - hblank_percentage); 2758c2ecf20Sopenharmony_ci hblank -= hblank % (2 * CVT_H_GRANULARITY); 2768c2ecf20Sopenharmony_ci /* 14. find the total pixels per line */ 2778c2ecf20Sopenharmony_ci drm_mode->htotal = drm_mode->hdisplay + hblank; 2788c2ecf20Sopenharmony_ci drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2; 2798c2ecf20Sopenharmony_ci drm_mode->hsync_start = drm_mode->hsync_end - 2808c2ecf20Sopenharmony_ci (drm_mode->htotal * CVT_HSYNC_PERCENTAGE) / 100; 2818c2ecf20Sopenharmony_ci drm_mode->hsync_start += CVT_H_GRANULARITY - 2828c2ecf20Sopenharmony_ci drm_mode->hsync_start % CVT_H_GRANULARITY; 2838c2ecf20Sopenharmony_ci /* fill the Vsync values */ 2848c2ecf20Sopenharmony_ci drm_mode->vsync_start = drm_mode->vdisplay + CVT_MIN_V_PORCH; 2858c2ecf20Sopenharmony_ci drm_mode->vsync_end = drm_mode->vsync_start + vsync; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci /* Reduced blanking */ 2888c2ecf20Sopenharmony_ci /* Minimum vertical blanking interval time (µs)- default 460 */ 2898c2ecf20Sopenharmony_ci#define CVT_RB_MIN_VBLANK 460 2908c2ecf20Sopenharmony_ci /* Fixed number of clocks for horizontal sync */ 2918c2ecf20Sopenharmony_ci#define CVT_RB_H_SYNC 32 2928c2ecf20Sopenharmony_ci /* Fixed number of clocks for horizontal blanking */ 2938c2ecf20Sopenharmony_ci#define CVT_RB_H_BLANK 160 2948c2ecf20Sopenharmony_ci /* Fixed number of lines for vertical front porch - default 3*/ 2958c2ecf20Sopenharmony_ci#define CVT_RB_VFPORCH 3 2968c2ecf20Sopenharmony_ci int vbilines; 2978c2ecf20Sopenharmony_ci int tmp1, tmp2; 2988c2ecf20Sopenharmony_ci /* 8. Estimate Horizontal period. */ 2998c2ecf20Sopenharmony_ci tmp1 = HV_FACTOR * 1000000 - 3008c2ecf20Sopenharmony_ci CVT_RB_MIN_VBLANK * HV_FACTOR * vfieldrate; 3018c2ecf20Sopenharmony_ci tmp2 = vdisplay_rnd + 2 * vmargin; 3028c2ecf20Sopenharmony_ci hperiod = tmp1 / (tmp2 * vfieldrate); 3038c2ecf20Sopenharmony_ci /* 9. Find number of lines in vertical blanking */ 3048c2ecf20Sopenharmony_ci vbilines = CVT_RB_MIN_VBLANK * HV_FACTOR / hperiod + 1; 3058c2ecf20Sopenharmony_ci /* 10. Check if vertical blanking is sufficient */ 3068c2ecf20Sopenharmony_ci if (vbilines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) 3078c2ecf20Sopenharmony_ci vbilines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; 3088c2ecf20Sopenharmony_ci /* 11. Find total number of lines in vertical field */ 3098c2ecf20Sopenharmony_ci drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + vbilines; 3108c2ecf20Sopenharmony_ci /* 12. Find total number of pixels in a line */ 3118c2ecf20Sopenharmony_ci drm_mode->htotal = drm_mode->hdisplay + CVT_RB_H_BLANK; 3128c2ecf20Sopenharmony_ci /* Fill in HSync values */ 3138c2ecf20Sopenharmony_ci drm_mode->hsync_end = drm_mode->hdisplay + CVT_RB_H_BLANK / 2; 3148c2ecf20Sopenharmony_ci drm_mode->hsync_start = drm_mode->hsync_end - CVT_RB_H_SYNC; 3158c2ecf20Sopenharmony_ci /* Fill in VSync values */ 3168c2ecf20Sopenharmony_ci drm_mode->vsync_start = drm_mode->vdisplay + CVT_RB_VFPORCH; 3178c2ecf20Sopenharmony_ci drm_mode->vsync_end = drm_mode->vsync_start + vsync; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci /* 15/13. Find pixel clock frequency (kHz for xf86) */ 3208c2ecf20Sopenharmony_ci tmp = drm_mode->htotal; /* perform intermediate calcs in u64 */ 3218c2ecf20Sopenharmony_ci tmp *= HV_FACTOR * 1000; 3228c2ecf20Sopenharmony_ci do_div(tmp, hperiod); 3238c2ecf20Sopenharmony_ci tmp -= drm_mode->clock % CVT_CLOCK_STEP; 3248c2ecf20Sopenharmony_ci drm_mode->clock = tmp; 3258c2ecf20Sopenharmony_ci /* 18/16. Find actual vertical frame frequency */ 3268c2ecf20Sopenharmony_ci /* ignore - just set the mode flag for interlaced */ 3278c2ecf20Sopenharmony_ci if (interlaced) { 3288c2ecf20Sopenharmony_ci drm_mode->vtotal *= 2; 3298c2ecf20Sopenharmony_ci drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci /* Fill the mode line name */ 3328c2ecf20Sopenharmony_ci drm_mode_set_name(drm_mode); 3338c2ecf20Sopenharmony_ci if (reduced) 3348c2ecf20Sopenharmony_ci drm_mode->flags |= (DRM_MODE_FLAG_PHSYNC | 3358c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NVSYNC); 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci drm_mode->flags |= (DRM_MODE_FLAG_PVSYNC | 3388c2ecf20Sopenharmony_ci DRM_MODE_FLAG_NHSYNC); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return drm_mode; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_cvt_mode); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm 3468c2ecf20Sopenharmony_ci * @dev: drm device 3478c2ecf20Sopenharmony_ci * @hdisplay: hdisplay size 3488c2ecf20Sopenharmony_ci * @vdisplay: vdisplay size 3498c2ecf20Sopenharmony_ci * @vrefresh: vrefresh rate. 3508c2ecf20Sopenharmony_ci * @interlaced: whether to compute an interlaced mode 3518c2ecf20Sopenharmony_ci * @margins: desired margin (borders) size 3528c2ecf20Sopenharmony_ci * @GTF_M: extended GTF formula parameters 3538c2ecf20Sopenharmony_ci * @GTF_2C: extended GTF formula parameters 3548c2ecf20Sopenharmony_ci * @GTF_K: extended GTF formula parameters 3558c2ecf20Sopenharmony_ci * @GTF_2J: extended GTF formula parameters 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * GTF feature blocks specify C and J in multiples of 0.5, so we pass them 3588c2ecf20Sopenharmony_ci * in here multiplied by two. For a C of 40, pass in 80. 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Returns: 3618c2ecf20Sopenharmony_ci * The modeline based on the full GTF algorithm stored in a drm_display_mode object. 3628c2ecf20Sopenharmony_ci * The display mode object is allocated with drm_mode_create(). Returns NULL 3638c2ecf20Sopenharmony_ci * when no mode could be allocated. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistruct drm_display_mode * 3668c2ecf20Sopenharmony_cidrm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, 3678c2ecf20Sopenharmony_ci int vrefresh, bool interlaced, int margins, 3688c2ecf20Sopenharmony_ci int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) 3698c2ecf20Sopenharmony_ci{ /* 1) top/bottom margin size (% of height) - default: 1.8, */ 3708c2ecf20Sopenharmony_ci#define GTF_MARGIN_PERCENTAGE 18 3718c2ecf20Sopenharmony_ci /* 2) character cell horizontal granularity (pixels) - default 8 */ 3728c2ecf20Sopenharmony_ci#define GTF_CELL_GRAN 8 3738c2ecf20Sopenharmony_ci /* 3) Minimum vertical porch (lines) - default 3 */ 3748c2ecf20Sopenharmony_ci#define GTF_MIN_V_PORCH 1 3758c2ecf20Sopenharmony_ci /* width of vsync in lines */ 3768c2ecf20Sopenharmony_ci#define V_SYNC_RQD 3 3778c2ecf20Sopenharmony_ci /* width of hsync as % of total line */ 3788c2ecf20Sopenharmony_ci#define H_SYNC_PERCENT 8 3798c2ecf20Sopenharmony_ci /* min time of vsync + back porch (microsec) */ 3808c2ecf20Sopenharmony_ci#define MIN_VSYNC_PLUS_BP 550 3818c2ecf20Sopenharmony_ci /* C' and M' are part of the Blanking Duty Cycle computation */ 3828c2ecf20Sopenharmony_ci#define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) 3838c2ecf20Sopenharmony_ci#define GTF_M_PRIME (GTF_K * GTF_M / 256) 3848c2ecf20Sopenharmony_ci struct drm_display_mode *drm_mode; 3858c2ecf20Sopenharmony_ci unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; 3868c2ecf20Sopenharmony_ci int top_margin, bottom_margin; 3878c2ecf20Sopenharmony_ci int interlace; 3888c2ecf20Sopenharmony_ci unsigned int hfreq_est; 3898c2ecf20Sopenharmony_ci int vsync_plus_bp, __maybe_unused vback_porch; 3908c2ecf20Sopenharmony_ci unsigned int vtotal_lines, __maybe_unused vfieldrate_est; 3918c2ecf20Sopenharmony_ci unsigned int __maybe_unused hperiod; 3928c2ecf20Sopenharmony_ci unsigned int vfield_rate, __maybe_unused vframe_rate; 3938c2ecf20Sopenharmony_ci int left_margin, right_margin; 3948c2ecf20Sopenharmony_ci unsigned int total_active_pixels, ideal_duty_cycle; 3958c2ecf20Sopenharmony_ci unsigned int hblank, total_pixels, pixel_freq; 3968c2ecf20Sopenharmony_ci int hsync, hfront_porch, vodd_front_porch_lines; 3978c2ecf20Sopenharmony_ci unsigned int tmp1, tmp2; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!hdisplay || !vdisplay) 4008c2ecf20Sopenharmony_ci return NULL; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci drm_mode = drm_mode_create(dev); 4038c2ecf20Sopenharmony_ci if (!drm_mode) 4048c2ecf20Sopenharmony_ci return NULL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* 1. In order to give correct results, the number of horizontal 4078c2ecf20Sopenharmony_ci * pixels requested is first processed to ensure that it is divisible 4088c2ecf20Sopenharmony_ci * by the character size, by rounding it to the nearest character 4098c2ecf20Sopenharmony_ci * cell boundary: 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; 4128c2ecf20Sopenharmony_ci hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 2. If interlace is requested, the number of vertical lines assumed 4158c2ecf20Sopenharmony_ci * by the calculation must be halved, as the computation calculates 4168c2ecf20Sopenharmony_ci * the number of vertical lines per field. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci if (interlaced) 4198c2ecf20Sopenharmony_ci vdisplay_rnd = vdisplay / 2; 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci vdisplay_rnd = vdisplay; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* 3. Find the frame rate required: */ 4248c2ecf20Sopenharmony_ci if (interlaced) 4258c2ecf20Sopenharmony_ci vfieldrate_rqd = vrefresh * 2; 4268c2ecf20Sopenharmony_ci else 4278c2ecf20Sopenharmony_ci vfieldrate_rqd = vrefresh; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* 4. Find number of lines in Top margin: */ 4308c2ecf20Sopenharmony_ci top_margin = 0; 4318c2ecf20Sopenharmony_ci if (margins) 4328c2ecf20Sopenharmony_ci top_margin = (vdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / 4338c2ecf20Sopenharmony_ci 1000; 4348c2ecf20Sopenharmony_ci /* 5. Find number of lines in bottom margin: */ 4358c2ecf20Sopenharmony_ci bottom_margin = top_margin; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* 6. If interlace is required, then set variable interlace: */ 4388c2ecf20Sopenharmony_ci if (interlaced) 4398c2ecf20Sopenharmony_ci interlace = 1; 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci interlace = 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 7. Estimate the Horizontal frequency */ 4448c2ecf20Sopenharmony_ci { 4458c2ecf20Sopenharmony_ci tmp1 = (1000000 - MIN_VSYNC_PLUS_BP * vfieldrate_rqd) / 500; 4468c2ecf20Sopenharmony_ci tmp2 = (vdisplay_rnd + 2 * top_margin + GTF_MIN_V_PORCH) * 4478c2ecf20Sopenharmony_ci 2 + interlace; 4488c2ecf20Sopenharmony_ci hfreq_est = (tmp2 * 1000 * vfieldrate_rqd) / tmp1; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 8. Find the number of lines in V sync + back porch */ 4528c2ecf20Sopenharmony_ci /* [V SYNC+BP] = RINT(([MIN VSYNC+BP] * hfreq_est / 1000000)) */ 4538c2ecf20Sopenharmony_ci vsync_plus_bp = MIN_VSYNC_PLUS_BP * hfreq_est / 1000; 4548c2ecf20Sopenharmony_ci vsync_plus_bp = (vsync_plus_bp + 500) / 1000; 4558c2ecf20Sopenharmony_ci /* 9. Find the number of lines in V back porch alone: */ 4568c2ecf20Sopenharmony_ci vback_porch = vsync_plus_bp - V_SYNC_RQD; 4578c2ecf20Sopenharmony_ci /* 10. Find the total number of lines in Vertical field period: */ 4588c2ecf20Sopenharmony_ci vtotal_lines = vdisplay_rnd + top_margin + bottom_margin + 4598c2ecf20Sopenharmony_ci vsync_plus_bp + GTF_MIN_V_PORCH; 4608c2ecf20Sopenharmony_ci /* 11. Estimate the Vertical field frequency: */ 4618c2ecf20Sopenharmony_ci vfieldrate_est = hfreq_est / vtotal_lines; 4628c2ecf20Sopenharmony_ci /* 12. Find the actual horizontal period: */ 4638c2ecf20Sopenharmony_ci hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* 13. Find the actual Vertical field frequency: */ 4668c2ecf20Sopenharmony_ci vfield_rate = hfreq_est / vtotal_lines; 4678c2ecf20Sopenharmony_ci /* 14. Find the Vertical frame frequency: */ 4688c2ecf20Sopenharmony_ci if (interlaced) 4698c2ecf20Sopenharmony_ci vframe_rate = vfield_rate / 2; 4708c2ecf20Sopenharmony_ci else 4718c2ecf20Sopenharmony_ci vframe_rate = vfield_rate; 4728c2ecf20Sopenharmony_ci /* 15. Find number of pixels in left margin: */ 4738c2ecf20Sopenharmony_ci if (margins) 4748c2ecf20Sopenharmony_ci left_margin = (hdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / 4758c2ecf20Sopenharmony_ci 1000; 4768c2ecf20Sopenharmony_ci else 4778c2ecf20Sopenharmony_ci left_margin = 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* 16.Find number of pixels in right margin: */ 4808c2ecf20Sopenharmony_ci right_margin = left_margin; 4818c2ecf20Sopenharmony_ci /* 17.Find total number of active pixels in image and left and right */ 4828c2ecf20Sopenharmony_ci total_active_pixels = hdisplay_rnd + left_margin + right_margin; 4838c2ecf20Sopenharmony_ci /* 18.Find the ideal blanking duty cycle from blanking duty cycle */ 4848c2ecf20Sopenharmony_ci ideal_duty_cycle = GTF_C_PRIME * 1000 - 4858c2ecf20Sopenharmony_ci (GTF_M_PRIME * 1000000 / hfreq_est); 4868c2ecf20Sopenharmony_ci /* 19.Find the number of pixels in the blanking time to the nearest 4878c2ecf20Sopenharmony_ci * double character cell: */ 4888c2ecf20Sopenharmony_ci hblank = total_active_pixels * ideal_duty_cycle / 4898c2ecf20Sopenharmony_ci (100000 - ideal_duty_cycle); 4908c2ecf20Sopenharmony_ci hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN); 4918c2ecf20Sopenharmony_ci hblank = hblank * 2 * GTF_CELL_GRAN; 4928c2ecf20Sopenharmony_ci /* 20.Find total number of pixels: */ 4938c2ecf20Sopenharmony_ci total_pixels = total_active_pixels + hblank; 4948c2ecf20Sopenharmony_ci /* 21.Find pixel clock frequency: */ 4958c2ecf20Sopenharmony_ci pixel_freq = total_pixels * hfreq_est / 1000; 4968c2ecf20Sopenharmony_ci /* Stage 1 computations are now complete; I should really pass 4978c2ecf20Sopenharmony_ci * the results to another function and do the Stage 2 computations, 4988c2ecf20Sopenharmony_ci * but I only need a few more values so I'll just append the 4998c2ecf20Sopenharmony_ci * computations here for now */ 5008c2ecf20Sopenharmony_ci /* 17. Find the number of pixels in the horizontal sync period: */ 5018c2ecf20Sopenharmony_ci hsync = H_SYNC_PERCENT * total_pixels / 100; 5028c2ecf20Sopenharmony_ci hsync = (hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; 5038c2ecf20Sopenharmony_ci hsync = hsync * GTF_CELL_GRAN; 5048c2ecf20Sopenharmony_ci /* 18. Find the number of pixels in horizontal front porch period */ 5058c2ecf20Sopenharmony_ci hfront_porch = hblank / 2 - hsync; 5068c2ecf20Sopenharmony_ci /* 36. Find the number of lines in the odd front porch period: */ 5078c2ecf20Sopenharmony_ci vodd_front_porch_lines = GTF_MIN_V_PORCH ; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* finally, pack the results in the mode struct */ 5108c2ecf20Sopenharmony_ci drm_mode->hdisplay = hdisplay_rnd; 5118c2ecf20Sopenharmony_ci drm_mode->hsync_start = hdisplay_rnd + hfront_porch; 5128c2ecf20Sopenharmony_ci drm_mode->hsync_end = drm_mode->hsync_start + hsync; 5138c2ecf20Sopenharmony_ci drm_mode->htotal = total_pixels; 5148c2ecf20Sopenharmony_ci drm_mode->vdisplay = vdisplay_rnd; 5158c2ecf20Sopenharmony_ci drm_mode->vsync_start = vdisplay_rnd + vodd_front_porch_lines; 5168c2ecf20Sopenharmony_ci drm_mode->vsync_end = drm_mode->vsync_start + V_SYNC_RQD; 5178c2ecf20Sopenharmony_ci drm_mode->vtotal = vtotal_lines; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci drm_mode->clock = pixel_freq; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (interlaced) { 5228c2ecf20Sopenharmony_ci drm_mode->vtotal *= 2; 5238c2ecf20Sopenharmony_ci drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci drm_mode_set_name(drm_mode); 5278c2ecf20Sopenharmony_ci if (GTF_M == 600 && GTF_2C == 80 && GTF_K == 128 && GTF_2J == 40) 5288c2ecf20Sopenharmony_ci drm_mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC; 5298c2ecf20Sopenharmony_ci else 5308c2ecf20Sopenharmony_ci drm_mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return drm_mode; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_gtf_mode_complex); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/** 5378c2ecf20Sopenharmony_ci * drm_gtf_mode - create the modeline based on the GTF algorithm 5388c2ecf20Sopenharmony_ci * @dev: drm device 5398c2ecf20Sopenharmony_ci * @hdisplay: hdisplay size 5408c2ecf20Sopenharmony_ci * @vdisplay: vdisplay size 5418c2ecf20Sopenharmony_ci * @vrefresh: vrefresh rate. 5428c2ecf20Sopenharmony_ci * @interlaced: whether to compute an interlaced mode 5438c2ecf20Sopenharmony_ci * @margins: desired margin (borders) size 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * return the modeline based on GTF algorithm 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * This function is to create the modeline based on the GTF algorithm. 5488c2ecf20Sopenharmony_ci * Generalized Timing Formula is derived from: 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * GTF Spreadsheet by Andy Morrish (1/5/97) 5518c2ecf20Sopenharmony_ci * available at https://www.vesa.org 5528c2ecf20Sopenharmony_ci * 5538c2ecf20Sopenharmony_ci * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. 5548c2ecf20Sopenharmony_ci * What I have done is to translate it by using integer calculation. 5558c2ecf20Sopenharmony_ci * I also refer to the function of fb_get_mode in the file of 5568c2ecf20Sopenharmony_ci * drivers/video/fbmon.c 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * Standard GTF parameters:: 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * M = 600 5618c2ecf20Sopenharmony_ci * C = 40 5628c2ecf20Sopenharmony_ci * K = 128 5638c2ecf20Sopenharmony_ci * J = 20 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Returns: 5668c2ecf20Sopenharmony_ci * The modeline based on the GTF algorithm stored in a drm_display_mode object. 5678c2ecf20Sopenharmony_ci * The display mode object is allocated with drm_mode_create(). Returns NULL 5688c2ecf20Sopenharmony_ci * when no mode could be allocated. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_cistruct drm_display_mode * 5718c2ecf20Sopenharmony_cidrm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, 5728c2ecf20Sopenharmony_ci bool interlaced, int margins) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, 5758c2ecf20Sopenharmony_ci interlaced, margins, 5768c2ecf20Sopenharmony_ci 600, 40 * 2, 128, 20 * 2); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_gtf_mode); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEOMODE_HELPERS 5818c2ecf20Sopenharmony_ci/** 5828c2ecf20Sopenharmony_ci * drm_display_mode_from_videomode - fill in @dmode using @vm, 5838c2ecf20Sopenharmony_ci * @vm: videomode structure to use as source 5848c2ecf20Sopenharmony_ci * @dmode: drm_display_mode structure to use as destination 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * Fills out @dmode using the display mode specified in @vm. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_civoid drm_display_mode_from_videomode(const struct videomode *vm, 5898c2ecf20Sopenharmony_ci struct drm_display_mode *dmode) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci dmode->hdisplay = vm->hactive; 5928c2ecf20Sopenharmony_ci dmode->hsync_start = dmode->hdisplay + vm->hfront_porch; 5938c2ecf20Sopenharmony_ci dmode->hsync_end = dmode->hsync_start + vm->hsync_len; 5948c2ecf20Sopenharmony_ci dmode->htotal = dmode->hsync_end + vm->hback_porch; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci dmode->vdisplay = vm->vactive; 5978c2ecf20Sopenharmony_ci dmode->vsync_start = dmode->vdisplay + vm->vfront_porch; 5988c2ecf20Sopenharmony_ci dmode->vsync_end = dmode->vsync_start + vm->vsync_len; 5998c2ecf20Sopenharmony_ci dmode->vtotal = dmode->vsync_end + vm->vback_porch; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci dmode->clock = vm->pixelclock / 1000; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci dmode->flags = 0; 6048c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) 6058c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_PHSYNC; 6068c2ecf20Sopenharmony_ci else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) 6078c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_NHSYNC; 6088c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) 6098c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_PVSYNC; 6108c2ecf20Sopenharmony_ci else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) 6118c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_NVSYNC; 6128c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_INTERLACED) 6138c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_INTERLACE; 6148c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) 6158c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_DBLSCAN; 6168c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) 6178c2ecf20Sopenharmony_ci dmode->flags |= DRM_MODE_FLAG_DBLCLK; 6188c2ecf20Sopenharmony_ci drm_mode_set_name(dmode); 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci/** 6238c2ecf20Sopenharmony_ci * drm_display_mode_to_videomode - fill in @vm using @dmode, 6248c2ecf20Sopenharmony_ci * @dmode: drm_display_mode structure to use as source 6258c2ecf20Sopenharmony_ci * @vm: videomode structure to use as destination 6268c2ecf20Sopenharmony_ci * 6278c2ecf20Sopenharmony_ci * Fills out @vm using the display mode specified in @dmode. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_civoid drm_display_mode_to_videomode(const struct drm_display_mode *dmode, 6308c2ecf20Sopenharmony_ci struct videomode *vm) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci vm->hactive = dmode->hdisplay; 6338c2ecf20Sopenharmony_ci vm->hfront_porch = dmode->hsync_start - dmode->hdisplay; 6348c2ecf20Sopenharmony_ci vm->hsync_len = dmode->hsync_end - dmode->hsync_start; 6358c2ecf20Sopenharmony_ci vm->hback_porch = dmode->htotal - dmode->hsync_end; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci vm->vactive = dmode->vdisplay; 6388c2ecf20Sopenharmony_ci vm->vfront_porch = dmode->vsync_start - dmode->vdisplay; 6398c2ecf20Sopenharmony_ci vm->vsync_len = dmode->vsync_end - dmode->vsync_start; 6408c2ecf20Sopenharmony_ci vm->vback_porch = dmode->vtotal - dmode->vsync_end; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci vm->pixelclock = dmode->clock * 1000; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci vm->flags = 0; 6458c2ecf20Sopenharmony_ci if (dmode->flags & DRM_MODE_FLAG_PHSYNC) 6468c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; 6478c2ecf20Sopenharmony_ci else if (dmode->flags & DRM_MODE_FLAG_NHSYNC) 6488c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; 6498c2ecf20Sopenharmony_ci if (dmode->flags & DRM_MODE_FLAG_PVSYNC) 6508c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; 6518c2ecf20Sopenharmony_ci else if (dmode->flags & DRM_MODE_FLAG_NVSYNC) 6528c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; 6538c2ecf20Sopenharmony_ci if (dmode->flags & DRM_MODE_FLAG_INTERLACE) 6548c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_INTERLACED; 6558c2ecf20Sopenharmony_ci if (dmode->flags & DRM_MODE_FLAG_DBLSCAN) 6568c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_DOUBLESCAN; 6578c2ecf20Sopenharmony_ci if (dmode->flags & DRM_MODE_FLAG_DBLCLK) 6588c2ecf20Sopenharmony_ci vm->flags |= DISPLAY_FLAGS_DOUBLECLK; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * drm_bus_flags_from_videomode - extract information about pixelclk and 6648c2ecf20Sopenharmony_ci * DE polarity from videomode and store it in a separate variable 6658c2ecf20Sopenharmony_ci * @vm: videomode structure to use 6668c2ecf20Sopenharmony_ci * @bus_flags: information about pixelclk, sync and DE polarity will be stored 6678c2ecf20Sopenharmony_ci * here 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE 6708c2ecf20Sopenharmony_ci * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS 6718c2ecf20Sopenharmony_ci * found in @vm 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_civoid drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci *bus_flags = 0; 6768c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 6778c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; 6788c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 6798c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) 6828c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; 6838c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) 6848c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_DE_LOW) 6878c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_DE_LOW; 6888c2ecf20Sopenharmony_ci if (vm->flags & DISPLAY_FLAGS_DE_HIGH) 6898c2ecf20Sopenharmony_ci *bus_flags |= DRM_BUS_FLAG_DE_HIGH; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6948c2ecf20Sopenharmony_ci/** 6958c2ecf20Sopenharmony_ci * of_get_drm_display_mode - get a drm_display_mode from devicetree 6968c2ecf20Sopenharmony_ci * @np: device_node with the timing specification 6978c2ecf20Sopenharmony_ci * @dmode: will be set to the return value 6988c2ecf20Sopenharmony_ci * @bus_flags: information about pixelclk, sync and DE polarity 6998c2ecf20Sopenharmony_ci * @index: index into the list of display timings in devicetree 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * This function is expensive and should only be used, if only one mode is to be 7028c2ecf20Sopenharmony_ci * read from DT. To get multiple modes start with of_get_display_timings and 7038c2ecf20Sopenharmony_ci * work with that instead. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * Returns: 7068c2ecf20Sopenharmony_ci * 0 on success, a negative errno code when no of videomode node was found. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ciint of_get_drm_display_mode(struct device_node *np, 7098c2ecf20Sopenharmony_ci struct drm_display_mode *dmode, u32 *bus_flags, 7108c2ecf20Sopenharmony_ci int index) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct videomode vm; 7138c2ecf20Sopenharmony_ci int ret; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ret = of_get_videomode(np, &vm, index); 7168c2ecf20Sopenharmony_ci if (ret) 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci drm_display_mode_from_videomode(&vm, dmode); 7208c2ecf20Sopenharmony_ci if (bus_flags) 7218c2ecf20Sopenharmony_ci drm_bus_flags_from_videomode(&vm, bus_flags); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci pr_debug("%pOF: got %dx%d display mode\n", 7248c2ecf20Sopenharmony_ci np, vm.hactive, vm.vactive); 7258c2ecf20Sopenharmony_ci drm_mode_debug_printmodeline(dmode); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_get_drm_display_mode); 7308c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 7318c2ecf20Sopenharmony_ci#endif /* CONFIG_VIDEOMODE_HELPERS */ 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/** 7348c2ecf20Sopenharmony_ci * drm_mode_set_name - set the name on a mode 7358c2ecf20Sopenharmony_ci * @mode: name will be set in this mode 7368c2ecf20Sopenharmony_ci * 7378c2ecf20Sopenharmony_ci * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay> 7388c2ecf20Sopenharmony_ci * with an optional 'i' suffix for interlaced modes. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_civoid drm_mode_set_name(struct drm_display_mode *mode) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s", 7458c2ecf20Sopenharmony_ci mode->hdisplay, mode->vdisplay, 7468c2ecf20Sopenharmony_ci interlaced ? "i" : ""); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_set_name); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/** 7518c2ecf20Sopenharmony_ci * drm_mode_vrefresh - get the vrefresh of a mode 7528c2ecf20Sopenharmony_ci * @mode: mode 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * Returns: 7558c2ecf20Sopenharmony_ci * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the 7568c2ecf20Sopenharmony_ci * value first if it is not yet set. 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_ciint drm_mode_vrefresh(const struct drm_display_mode *mode) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci unsigned int num, den; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (mode->htotal == 0 || mode->vtotal == 0) 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci num = mode->clock; 7668c2ecf20Sopenharmony_ci den = mode->htotal * mode->vtotal; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 7698c2ecf20Sopenharmony_ci num *= 2; 7708c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 7718c2ecf20Sopenharmony_ci den *= 2; 7728c2ecf20Sopenharmony_ci if (mode->vscan > 1) 7738c2ecf20Sopenharmony_ci den *= mode->vscan; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_vrefresh); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci/** 7808c2ecf20Sopenharmony_ci * drm_mode_get_hv_timing - Fetches hdisplay/vdisplay for given mode 7818c2ecf20Sopenharmony_ci * @mode: mode to query 7828c2ecf20Sopenharmony_ci * @hdisplay: hdisplay value to fill in 7838c2ecf20Sopenharmony_ci * @vdisplay: vdisplay value to fill in 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * The vdisplay value will be doubled if the specified mode is a stereo mode of 7868c2ecf20Sopenharmony_ci * the appropriate layout. 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_civoid drm_mode_get_hv_timing(const struct drm_display_mode *mode, 7898c2ecf20Sopenharmony_ci int *hdisplay, int *vdisplay) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct drm_display_mode adjusted = *mode; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); 7948c2ecf20Sopenharmony_ci *hdisplay = adjusted.crtc_hdisplay; 7958c2ecf20Sopenharmony_ci *vdisplay = adjusted.crtc_vdisplay; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_get_hv_timing); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci/** 8008c2ecf20Sopenharmony_ci * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters 8018c2ecf20Sopenharmony_ci * @p: mode 8028c2ecf20Sopenharmony_ci * @adjust_flags: a combination of adjustment flags 8038c2ecf20Sopenharmony_ci * 8048c2ecf20Sopenharmony_ci * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of 8078c2ecf20Sopenharmony_ci * interlaced modes. 8088c2ecf20Sopenharmony_ci * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for 8098c2ecf20Sopenharmony_ci * buffers containing two eyes (only adjust the timings when needed, eg. for 8108c2ecf20Sopenharmony_ci * "frame packing" or "side by side full"). 8118c2ecf20Sopenharmony_ci * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* 8128c2ecf20Sopenharmony_ci * be performed for doublescan and vscan > 1 modes respectively. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_civoid drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci if (!p) 8178c2ecf20Sopenharmony_ci return; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci p->crtc_clock = p->clock; 8208c2ecf20Sopenharmony_ci p->crtc_hdisplay = p->hdisplay; 8218c2ecf20Sopenharmony_ci p->crtc_hsync_start = p->hsync_start; 8228c2ecf20Sopenharmony_ci p->crtc_hsync_end = p->hsync_end; 8238c2ecf20Sopenharmony_ci p->crtc_htotal = p->htotal; 8248c2ecf20Sopenharmony_ci p->crtc_hskew = p->hskew; 8258c2ecf20Sopenharmony_ci p->crtc_vdisplay = p->vdisplay; 8268c2ecf20Sopenharmony_ci p->crtc_vsync_start = p->vsync_start; 8278c2ecf20Sopenharmony_ci p->crtc_vsync_end = p->vsync_end; 8288c2ecf20Sopenharmony_ci p->crtc_vtotal = p->vtotal; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (p->flags & DRM_MODE_FLAG_INTERLACE) { 8318c2ecf20Sopenharmony_ci if (adjust_flags & CRTC_INTERLACE_HALVE_V) { 8328c2ecf20Sopenharmony_ci p->crtc_vdisplay /= 2; 8338c2ecf20Sopenharmony_ci p->crtc_vsync_start /= 2; 8348c2ecf20Sopenharmony_ci p->crtc_vsync_end /= 2; 8358c2ecf20Sopenharmony_ci p->crtc_vtotal /= 2; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (!(adjust_flags & CRTC_NO_DBLSCAN)) { 8408c2ecf20Sopenharmony_ci if (p->flags & DRM_MODE_FLAG_DBLSCAN) { 8418c2ecf20Sopenharmony_ci p->crtc_vdisplay *= 2; 8428c2ecf20Sopenharmony_ci p->crtc_vsync_start *= 2; 8438c2ecf20Sopenharmony_ci p->crtc_vsync_end *= 2; 8448c2ecf20Sopenharmony_ci p->crtc_vtotal *= 2; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (!(adjust_flags & CRTC_NO_VSCAN)) { 8498c2ecf20Sopenharmony_ci if (p->vscan > 1) { 8508c2ecf20Sopenharmony_ci p->crtc_vdisplay *= p->vscan; 8518c2ecf20Sopenharmony_ci p->crtc_vsync_start *= p->vscan; 8528c2ecf20Sopenharmony_ci p->crtc_vsync_end *= p->vscan; 8538c2ecf20Sopenharmony_ci p->crtc_vtotal *= p->vscan; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (adjust_flags & CRTC_STEREO_DOUBLE) { 8588c2ecf20Sopenharmony_ci unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci switch (layout) { 8618c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_3D_FRAME_PACKING: 8628c2ecf20Sopenharmony_ci p->crtc_clock *= 2; 8638c2ecf20Sopenharmony_ci p->crtc_vdisplay += p->crtc_vtotal; 8648c2ecf20Sopenharmony_ci p->crtc_vsync_start += p->crtc_vtotal; 8658c2ecf20Sopenharmony_ci p->crtc_vsync_end += p->crtc_vtotal; 8668c2ecf20Sopenharmony_ci p->crtc_vtotal += p->crtc_vtotal; 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); 8728c2ecf20Sopenharmony_ci p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); 8738c2ecf20Sopenharmony_ci p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); 8748c2ecf20Sopenharmony_ci p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_set_crtcinfo); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci/** 8798c2ecf20Sopenharmony_ci * drm_mode_copy - copy the mode 8808c2ecf20Sopenharmony_ci * @dst: mode to overwrite 8818c2ecf20Sopenharmony_ci * @src: mode to copy 8828c2ecf20Sopenharmony_ci * 8838c2ecf20Sopenharmony_ci * Copy an existing mode into another mode, preserving the object id and 8848c2ecf20Sopenharmony_ci * list head of the destination mode. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_civoid drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct list_head head = dst->head; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci *dst = *src; 8918c2ecf20Sopenharmony_ci dst->head = head; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_copy); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/** 8968c2ecf20Sopenharmony_ci * drm_mode_duplicate - allocate and duplicate an existing mode 8978c2ecf20Sopenharmony_ci * @dev: drm_device to allocate the duplicated mode for 8988c2ecf20Sopenharmony_ci * @mode: mode to duplicate 8998c2ecf20Sopenharmony_ci * 9008c2ecf20Sopenharmony_ci * Just allocate a new mode, copy the existing mode into it, and return 9018c2ecf20Sopenharmony_ci * a pointer to it. Used to create new instances of established modes. 9028c2ecf20Sopenharmony_ci * 9038c2ecf20Sopenharmony_ci * Returns: 9048c2ecf20Sopenharmony_ci * Pointer to duplicated mode on success, NULL on error. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_cistruct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, 9078c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct drm_display_mode *nmode; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci nmode = drm_mode_create(dev); 9128c2ecf20Sopenharmony_ci if (!nmode) 9138c2ecf20Sopenharmony_ci return NULL; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci drm_mode_copy(nmode, mode); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return nmode; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_duplicate); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic bool drm_mode_match_timings(const struct drm_display_mode *mode1, 9228c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci return mode1->hdisplay == mode2->hdisplay && 9258c2ecf20Sopenharmony_ci mode1->hsync_start == mode2->hsync_start && 9268c2ecf20Sopenharmony_ci mode1->hsync_end == mode2->hsync_end && 9278c2ecf20Sopenharmony_ci mode1->htotal == mode2->htotal && 9288c2ecf20Sopenharmony_ci mode1->hskew == mode2->hskew && 9298c2ecf20Sopenharmony_ci mode1->vdisplay == mode2->vdisplay && 9308c2ecf20Sopenharmony_ci mode1->vsync_start == mode2->vsync_start && 9318c2ecf20Sopenharmony_ci mode1->vsync_end == mode2->vsync_end && 9328c2ecf20Sopenharmony_ci mode1->vtotal == mode2->vtotal && 9338c2ecf20Sopenharmony_ci mode1->vscan == mode2->vscan; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic bool drm_mode_match_clock(const struct drm_display_mode *mode1, 9378c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci /* 9408c2ecf20Sopenharmony_ci * do clock check convert to PICOS 9418c2ecf20Sopenharmony_ci * so fb modes get matched the same 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci if (mode1->clock && mode2->clock) 9448c2ecf20Sopenharmony_ci return KHZ2PICOS(mode1->clock) == KHZ2PICOS(mode2->clock); 9458c2ecf20Sopenharmony_ci else 9468c2ecf20Sopenharmony_ci return mode1->clock == mode2->clock; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic bool drm_mode_match_flags(const struct drm_display_mode *mode1, 9508c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci return (mode1->flags & ~DRM_MODE_FLAG_3D_MASK) == 9538c2ecf20Sopenharmony_ci (mode2->flags & ~DRM_MODE_FLAG_3D_MASK); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic bool drm_mode_match_3d_flags(const struct drm_display_mode *mode1, 9578c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci return (mode1->flags & DRM_MODE_FLAG_3D_MASK) == 9608c2ecf20Sopenharmony_ci (mode2->flags & DRM_MODE_FLAG_3D_MASK); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic bool drm_mode_match_aspect_ratio(const struct drm_display_mode *mode1, 9648c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci return mode1->picture_aspect_ratio == mode2->picture_aspect_ratio; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci/** 9708c2ecf20Sopenharmony_ci * drm_mode_match - test modes for (partial) equality 9718c2ecf20Sopenharmony_ci * @mode1: first mode 9728c2ecf20Sopenharmony_ci * @mode2: second mode 9738c2ecf20Sopenharmony_ci * @match_flags: which parts need to match (DRM_MODE_MATCH_*) 9748c2ecf20Sopenharmony_ci * 9758c2ecf20Sopenharmony_ci * Check to see if @mode1 and @mode2 are equivalent. 9768c2ecf20Sopenharmony_ci * 9778c2ecf20Sopenharmony_ci * Returns: 9788c2ecf20Sopenharmony_ci * True if the modes are (partially) equal, false otherwise. 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_cibool drm_mode_match(const struct drm_display_mode *mode1, 9818c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2, 9828c2ecf20Sopenharmony_ci unsigned int match_flags) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci if (!mode1 && !mode2) 9858c2ecf20Sopenharmony_ci return true; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (!mode1 || !mode2) 9888c2ecf20Sopenharmony_ci return false; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (match_flags & DRM_MODE_MATCH_TIMINGS && 9918c2ecf20Sopenharmony_ci !drm_mode_match_timings(mode1, mode2)) 9928c2ecf20Sopenharmony_ci return false; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (match_flags & DRM_MODE_MATCH_CLOCK && 9958c2ecf20Sopenharmony_ci !drm_mode_match_clock(mode1, mode2)) 9968c2ecf20Sopenharmony_ci return false; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (match_flags & DRM_MODE_MATCH_FLAGS && 9998c2ecf20Sopenharmony_ci !drm_mode_match_flags(mode1, mode2)) 10008c2ecf20Sopenharmony_ci return false; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (match_flags & DRM_MODE_MATCH_3D_FLAGS && 10038c2ecf20Sopenharmony_ci !drm_mode_match_3d_flags(mode1, mode2)) 10048c2ecf20Sopenharmony_ci return false; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (match_flags & DRM_MODE_MATCH_ASPECT_RATIO && 10078c2ecf20Sopenharmony_ci !drm_mode_match_aspect_ratio(mode1, mode2)) 10088c2ecf20Sopenharmony_ci return false; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return true; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_match); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/** 10158c2ecf20Sopenharmony_ci * drm_mode_equal - test modes for equality 10168c2ecf20Sopenharmony_ci * @mode1: first mode 10178c2ecf20Sopenharmony_ci * @mode2: second mode 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * Check to see if @mode1 and @mode2 are equivalent. 10208c2ecf20Sopenharmony_ci * 10218c2ecf20Sopenharmony_ci * Returns: 10228c2ecf20Sopenharmony_ci * True if the modes are equal, false otherwise. 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_cibool drm_mode_equal(const struct drm_display_mode *mode1, 10258c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci return drm_mode_match(mode1, mode2, 10288c2ecf20Sopenharmony_ci DRM_MODE_MATCH_TIMINGS | 10298c2ecf20Sopenharmony_ci DRM_MODE_MATCH_CLOCK | 10308c2ecf20Sopenharmony_ci DRM_MODE_MATCH_FLAGS | 10318c2ecf20Sopenharmony_ci DRM_MODE_MATCH_3D_FLAGS| 10328c2ecf20Sopenharmony_ci DRM_MODE_MATCH_ASPECT_RATIO); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_equal); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/** 10378c2ecf20Sopenharmony_ci * drm_mode_equal_no_clocks - test modes for equality 10388c2ecf20Sopenharmony_ci * @mode1: first mode 10398c2ecf20Sopenharmony_ci * @mode2: second mode 10408c2ecf20Sopenharmony_ci * 10418c2ecf20Sopenharmony_ci * Check to see if @mode1 and @mode2 are equivalent, but 10428c2ecf20Sopenharmony_ci * don't check the pixel clocks. 10438c2ecf20Sopenharmony_ci * 10448c2ecf20Sopenharmony_ci * Returns: 10458c2ecf20Sopenharmony_ci * True if the modes are equal, false otherwise. 10468c2ecf20Sopenharmony_ci */ 10478c2ecf20Sopenharmony_cibool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, 10488c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci return drm_mode_match(mode1, mode2, 10518c2ecf20Sopenharmony_ci DRM_MODE_MATCH_TIMINGS | 10528c2ecf20Sopenharmony_ci DRM_MODE_MATCH_FLAGS | 10538c2ecf20Sopenharmony_ci DRM_MODE_MATCH_3D_FLAGS); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_equal_no_clocks); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/** 10588c2ecf20Sopenharmony_ci * drm_mode_equal_no_clocks_no_stereo - test modes for equality 10598c2ecf20Sopenharmony_ci * @mode1: first mode 10608c2ecf20Sopenharmony_ci * @mode2: second mode 10618c2ecf20Sopenharmony_ci * 10628c2ecf20Sopenharmony_ci * Check to see if @mode1 and @mode2 are equivalent, but 10638c2ecf20Sopenharmony_ci * don't check the pixel clocks nor the stereo layout. 10648c2ecf20Sopenharmony_ci * 10658c2ecf20Sopenharmony_ci * Returns: 10668c2ecf20Sopenharmony_ci * True if the modes are equal, false otherwise. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_cibool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, 10698c2ecf20Sopenharmony_ci const struct drm_display_mode *mode2) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci return drm_mode_match(mode1, mode2, 10728c2ecf20Sopenharmony_ci DRM_MODE_MATCH_TIMINGS | 10738c2ecf20Sopenharmony_ci DRM_MODE_MATCH_FLAGS); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic enum drm_mode_status 10788c2ecf20Sopenharmony_cidrm_mode_validate_basic(const struct drm_display_mode *mode) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci if (mode->type & ~DRM_MODE_TYPE_ALL) 10818c2ecf20Sopenharmony_ci return MODE_BAD; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (mode->flags & ~DRM_MODE_FLAG_ALL) 10848c2ecf20Sopenharmony_ci return MODE_BAD; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) 10878c2ecf20Sopenharmony_ci return MODE_BAD; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (mode->clock == 0) 10908c2ecf20Sopenharmony_ci return MODE_CLOCK_LOW; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (mode->hdisplay == 0 || 10938c2ecf20Sopenharmony_ci mode->hsync_start < mode->hdisplay || 10948c2ecf20Sopenharmony_ci mode->hsync_end < mode->hsync_start || 10958c2ecf20Sopenharmony_ci mode->htotal < mode->hsync_end) 10968c2ecf20Sopenharmony_ci return MODE_H_ILLEGAL; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (mode->vdisplay == 0 || 10998c2ecf20Sopenharmony_ci mode->vsync_start < mode->vdisplay || 11008c2ecf20Sopenharmony_ci mode->vsync_end < mode->vsync_start || 11018c2ecf20Sopenharmony_ci mode->vtotal < mode->vsync_end) 11028c2ecf20Sopenharmony_ci return MODE_V_ILLEGAL; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci return MODE_OK; 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci/** 11088c2ecf20Sopenharmony_ci * drm_mode_validate_driver - make sure the mode is somewhat sane 11098c2ecf20Sopenharmony_ci * @dev: drm device 11108c2ecf20Sopenharmony_ci * @mode: mode to check 11118c2ecf20Sopenharmony_ci * 11128c2ecf20Sopenharmony_ci * First do basic validation on the mode, and then allow the driver 11138c2ecf20Sopenharmony_ci * to check for device/driver specific limitations via the optional 11148c2ecf20Sopenharmony_ci * &drm_mode_config_helper_funcs.mode_valid hook. 11158c2ecf20Sopenharmony_ci * 11168c2ecf20Sopenharmony_ci * Returns: 11178c2ecf20Sopenharmony_ci * The mode status 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_cienum drm_mode_status 11208c2ecf20Sopenharmony_cidrm_mode_validate_driver(struct drm_device *dev, 11218c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci enum drm_mode_status status; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci status = drm_mode_validate_basic(mode); 11268c2ecf20Sopenharmony_ci if (status != MODE_OK) 11278c2ecf20Sopenharmony_ci return status; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (dev->mode_config.funcs->mode_valid) 11308c2ecf20Sopenharmony_ci return dev->mode_config.funcs->mode_valid(dev, mode); 11318c2ecf20Sopenharmony_ci else 11328c2ecf20Sopenharmony_ci return MODE_OK; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_validate_driver); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci/** 11378c2ecf20Sopenharmony_ci * drm_mode_validate_size - make sure modes adhere to size constraints 11388c2ecf20Sopenharmony_ci * @mode: mode to check 11398c2ecf20Sopenharmony_ci * @maxX: maximum width 11408c2ecf20Sopenharmony_ci * @maxY: maximum height 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * This function is a helper which can be used to validate modes against size 11438c2ecf20Sopenharmony_ci * limitations of the DRM device/connector. If a mode is too big its status 11448c2ecf20Sopenharmony_ci * member is updated with the appropriate validation failure code. The list 11458c2ecf20Sopenharmony_ci * itself is not changed. 11468c2ecf20Sopenharmony_ci * 11478c2ecf20Sopenharmony_ci * Returns: 11488c2ecf20Sopenharmony_ci * The mode status 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_cienum drm_mode_status 11518c2ecf20Sopenharmony_cidrm_mode_validate_size(const struct drm_display_mode *mode, 11528c2ecf20Sopenharmony_ci int maxX, int maxY) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci if (maxX > 0 && mode->hdisplay > maxX) 11558c2ecf20Sopenharmony_ci return MODE_VIRTUAL_X; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (maxY > 0 && mode->vdisplay > maxY) 11588c2ecf20Sopenharmony_ci return MODE_VIRTUAL_Y; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return MODE_OK; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_validate_size); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/** 11658c2ecf20Sopenharmony_ci * drm_mode_validate_ycbcr420 - add 'ycbcr420-only' modes only when allowed 11668c2ecf20Sopenharmony_ci * @mode: mode to check 11678c2ecf20Sopenharmony_ci * @connector: drm connector under action 11688c2ecf20Sopenharmony_ci * 11698c2ecf20Sopenharmony_ci * This function is a helper which can be used to filter out any YCBCR420 11708c2ecf20Sopenharmony_ci * only mode, when the source doesn't support it. 11718c2ecf20Sopenharmony_ci * 11728c2ecf20Sopenharmony_ci * Returns: 11738c2ecf20Sopenharmony_ci * The mode status 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_cienum drm_mode_status 11768c2ecf20Sopenharmony_cidrm_mode_validate_ycbcr420(const struct drm_display_mode *mode, 11778c2ecf20Sopenharmony_ci struct drm_connector *connector) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci u8 vic = drm_match_cea_mode(mode); 11808c2ecf20Sopenharmony_ci enum drm_mode_status status = MODE_OK; 11818c2ecf20Sopenharmony_ci struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (test_bit(vic, hdmi->y420_vdb_modes)) { 11848c2ecf20Sopenharmony_ci if (!connector->ycbcr_420_allowed) 11858c2ecf20Sopenharmony_ci status = MODE_NO_420; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return status; 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_validate_ycbcr420); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci#define MODE_STATUS(status) [MODE_ ## status + 3] = #status 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic const char * const drm_mode_status_names[] = { 11958c2ecf20Sopenharmony_ci MODE_STATUS(OK), 11968c2ecf20Sopenharmony_ci MODE_STATUS(HSYNC), 11978c2ecf20Sopenharmony_ci MODE_STATUS(VSYNC), 11988c2ecf20Sopenharmony_ci MODE_STATUS(H_ILLEGAL), 11998c2ecf20Sopenharmony_ci MODE_STATUS(V_ILLEGAL), 12008c2ecf20Sopenharmony_ci MODE_STATUS(BAD_WIDTH), 12018c2ecf20Sopenharmony_ci MODE_STATUS(NOMODE), 12028c2ecf20Sopenharmony_ci MODE_STATUS(NO_INTERLACE), 12038c2ecf20Sopenharmony_ci MODE_STATUS(NO_DBLESCAN), 12048c2ecf20Sopenharmony_ci MODE_STATUS(NO_VSCAN), 12058c2ecf20Sopenharmony_ci MODE_STATUS(MEM), 12068c2ecf20Sopenharmony_ci MODE_STATUS(VIRTUAL_X), 12078c2ecf20Sopenharmony_ci MODE_STATUS(VIRTUAL_Y), 12088c2ecf20Sopenharmony_ci MODE_STATUS(MEM_VIRT), 12098c2ecf20Sopenharmony_ci MODE_STATUS(NOCLOCK), 12108c2ecf20Sopenharmony_ci MODE_STATUS(CLOCK_HIGH), 12118c2ecf20Sopenharmony_ci MODE_STATUS(CLOCK_LOW), 12128c2ecf20Sopenharmony_ci MODE_STATUS(CLOCK_RANGE), 12138c2ecf20Sopenharmony_ci MODE_STATUS(BAD_HVALUE), 12148c2ecf20Sopenharmony_ci MODE_STATUS(BAD_VVALUE), 12158c2ecf20Sopenharmony_ci MODE_STATUS(BAD_VSCAN), 12168c2ecf20Sopenharmony_ci MODE_STATUS(HSYNC_NARROW), 12178c2ecf20Sopenharmony_ci MODE_STATUS(HSYNC_WIDE), 12188c2ecf20Sopenharmony_ci MODE_STATUS(HBLANK_NARROW), 12198c2ecf20Sopenharmony_ci MODE_STATUS(HBLANK_WIDE), 12208c2ecf20Sopenharmony_ci MODE_STATUS(VSYNC_NARROW), 12218c2ecf20Sopenharmony_ci MODE_STATUS(VSYNC_WIDE), 12228c2ecf20Sopenharmony_ci MODE_STATUS(VBLANK_NARROW), 12238c2ecf20Sopenharmony_ci MODE_STATUS(VBLANK_WIDE), 12248c2ecf20Sopenharmony_ci MODE_STATUS(PANEL), 12258c2ecf20Sopenharmony_ci MODE_STATUS(INTERLACE_WIDTH), 12268c2ecf20Sopenharmony_ci MODE_STATUS(ONE_WIDTH), 12278c2ecf20Sopenharmony_ci MODE_STATUS(ONE_HEIGHT), 12288c2ecf20Sopenharmony_ci MODE_STATUS(ONE_SIZE), 12298c2ecf20Sopenharmony_ci MODE_STATUS(NO_REDUCED), 12308c2ecf20Sopenharmony_ci MODE_STATUS(NO_STEREO), 12318c2ecf20Sopenharmony_ci MODE_STATUS(NO_420), 12328c2ecf20Sopenharmony_ci MODE_STATUS(STALE), 12338c2ecf20Sopenharmony_ci MODE_STATUS(BAD), 12348c2ecf20Sopenharmony_ci MODE_STATUS(ERROR), 12358c2ecf20Sopenharmony_ci}; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci#undef MODE_STATUS 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ciconst char *drm_get_mode_status_name(enum drm_mode_status status) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci int index = status + 3; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (WARN_ON(index < 0 || index >= ARRAY_SIZE(drm_mode_status_names))) 12448c2ecf20Sopenharmony_ci return ""; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return drm_mode_status_names[index]; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci/** 12508c2ecf20Sopenharmony_ci * drm_mode_prune_invalid - remove invalid modes from mode list 12518c2ecf20Sopenharmony_ci * @dev: DRM device 12528c2ecf20Sopenharmony_ci * @mode_list: list of modes to check 12538c2ecf20Sopenharmony_ci * @verbose: be verbose about it 12548c2ecf20Sopenharmony_ci * 12558c2ecf20Sopenharmony_ci * This helper function can be used to prune a display mode list after 12568c2ecf20Sopenharmony_ci * validation has been completed. All modes whose status is not MODE_OK will be 12578c2ecf20Sopenharmony_ci * removed from the list, and if @verbose the status code and mode name is also 12588c2ecf20Sopenharmony_ci * printed to dmesg. 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_civoid drm_mode_prune_invalid(struct drm_device *dev, 12618c2ecf20Sopenharmony_ci struct list_head *mode_list, bool verbose) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct drm_display_mode *mode, *t; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci list_for_each_entry_safe(mode, t, mode_list, head) { 12668c2ecf20Sopenharmony_ci if (mode->status != MODE_OK) { 12678c2ecf20Sopenharmony_ci list_del(&mode->head); 12688c2ecf20Sopenharmony_ci if (verbose) { 12698c2ecf20Sopenharmony_ci drm_mode_debug_printmodeline(mode); 12708c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Not using %s mode: %s\n", 12718c2ecf20Sopenharmony_ci mode->name, 12728c2ecf20Sopenharmony_ci drm_get_mode_status_name(mode->status)); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci drm_mode_destroy(dev, mode); 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_prune_invalid); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci/** 12818c2ecf20Sopenharmony_ci * drm_mode_compare - compare modes for favorability 12828c2ecf20Sopenharmony_ci * @priv: unused 12838c2ecf20Sopenharmony_ci * @lh_a: list_head for first mode 12848c2ecf20Sopenharmony_ci * @lh_b: list_head for second mode 12858c2ecf20Sopenharmony_ci * 12868c2ecf20Sopenharmony_ci * Compare two modes, given by @lh_a and @lh_b, returning a value indicating 12878c2ecf20Sopenharmony_ci * which is better. 12888c2ecf20Sopenharmony_ci * 12898c2ecf20Sopenharmony_ci * Returns: 12908c2ecf20Sopenharmony_ci * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or 12918c2ecf20Sopenharmony_ci * positive if @lh_b is better than @lh_a. 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_cistatic int drm_mode_compare(void *priv, const struct list_head *lh_a, 12948c2ecf20Sopenharmony_ci const struct list_head *lh_b) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); 12978c2ecf20Sopenharmony_ci struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); 12988c2ecf20Sopenharmony_ci int diff; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - 13018c2ecf20Sopenharmony_ci ((a->type & DRM_MODE_TYPE_PREFERRED) != 0); 13028c2ecf20Sopenharmony_ci if (diff) 13038c2ecf20Sopenharmony_ci return diff; 13048c2ecf20Sopenharmony_ci diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; 13058c2ecf20Sopenharmony_ci if (diff) 13068c2ecf20Sopenharmony_ci return diff; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci diff = drm_mode_vrefresh(b) - drm_mode_vrefresh(a); 13098c2ecf20Sopenharmony_ci if (diff) 13108c2ecf20Sopenharmony_ci return diff; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci diff = b->clock - a->clock; 13138c2ecf20Sopenharmony_ci return diff; 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci/** 13178c2ecf20Sopenharmony_ci * drm_mode_sort - sort mode list 13188c2ecf20Sopenharmony_ci * @mode_list: list of drm_display_mode structures to sort 13198c2ecf20Sopenharmony_ci * 13208c2ecf20Sopenharmony_ci * Sort @mode_list by favorability, moving good modes to the head of the list. 13218c2ecf20Sopenharmony_ci */ 13228c2ecf20Sopenharmony_civoid drm_mode_sort(struct list_head *mode_list) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci list_sort(NULL, mode_list, drm_mode_compare); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_sort); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci/** 13298c2ecf20Sopenharmony_ci * drm_connector_list_update - update the mode list for the connector 13308c2ecf20Sopenharmony_ci * @connector: the connector to update 13318c2ecf20Sopenharmony_ci * 13328c2ecf20Sopenharmony_ci * This moves the modes from the @connector probed_modes list 13338c2ecf20Sopenharmony_ci * to the actual mode list. It compares the probed mode against the current 13348c2ecf20Sopenharmony_ci * list and only adds different/new modes. 13358c2ecf20Sopenharmony_ci * 13368c2ecf20Sopenharmony_ci * This is just a helper functions doesn't validate any modes itself and also 13378c2ecf20Sopenharmony_ci * doesn't prune any invalid modes. Callers need to do that themselves. 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_civoid drm_connector_list_update(struct drm_connector *connector) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci struct drm_display_mode *pmode, *pt; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) { 13468c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 13478c2ecf20Sopenharmony_ci bool found_it = false; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci /* go through current modes checking for the new probed mode */ 13508c2ecf20Sopenharmony_ci list_for_each_entry(mode, &connector->modes, head) { 13518c2ecf20Sopenharmony_ci if (!drm_mode_equal(pmode, mode)) 13528c2ecf20Sopenharmony_ci continue; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci found_it = true; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * If the old matching mode is stale (ie. left over 13588c2ecf20Sopenharmony_ci * from a previous probe) just replace it outright. 13598c2ecf20Sopenharmony_ci * Otherwise just merge the type bits between all 13608c2ecf20Sopenharmony_ci * equal probed modes. 13618c2ecf20Sopenharmony_ci * 13628c2ecf20Sopenharmony_ci * If two probed modes are considered equal, pick the 13638c2ecf20Sopenharmony_ci * actual timings from the one that's marked as 13648c2ecf20Sopenharmony_ci * preferred (in case the match isn't 100%). If 13658c2ecf20Sopenharmony_ci * multiple or zero preferred modes are present, favor 13668c2ecf20Sopenharmony_ci * the mode added to the probed_modes list first. 13678c2ecf20Sopenharmony_ci */ 13688c2ecf20Sopenharmony_ci if (mode->status == MODE_STALE) { 13698c2ecf20Sopenharmony_ci drm_mode_copy(mode, pmode); 13708c2ecf20Sopenharmony_ci } else if ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 && 13718c2ecf20Sopenharmony_ci (pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) { 13728c2ecf20Sopenharmony_ci pmode->type |= mode->type; 13738c2ecf20Sopenharmony_ci drm_mode_copy(mode, pmode); 13748c2ecf20Sopenharmony_ci } else { 13758c2ecf20Sopenharmony_ci mode->type |= pmode->type; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci list_del(&pmode->head); 13798c2ecf20Sopenharmony_ci drm_mode_destroy(connector->dev, pmode); 13808c2ecf20Sopenharmony_ci break; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (!found_it) { 13848c2ecf20Sopenharmony_ci list_move_tail(&pmode->head, &connector->modes); 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_connector_list_update); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr, 13918c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci unsigned int bpp; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci if (str[0] != '-') 13968c2ecf20Sopenharmony_ci return -EINVAL; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci str++; 13998c2ecf20Sopenharmony_ci bpp = simple_strtol(str, end_ptr, 10); 14008c2ecf20Sopenharmony_ci if (*end_ptr == str) 14018c2ecf20Sopenharmony_ci return -EINVAL; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci mode->bpp = bpp; 14048c2ecf20Sopenharmony_ci mode->bpp_specified = true; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr, 14108c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci unsigned int refresh; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (str[0] != '@') 14158c2ecf20Sopenharmony_ci return -EINVAL; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci str++; 14188c2ecf20Sopenharmony_ci refresh = simple_strtol(str, end_ptr, 10); 14198c2ecf20Sopenharmony_ci if (*end_ptr == str) 14208c2ecf20Sopenharmony_ci return -EINVAL; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci mode->refresh = refresh; 14238c2ecf20Sopenharmony_ci mode->refresh_specified = true; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci return 0; 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_extra(const char *str, int length, 14298c2ecf20Sopenharmony_ci bool freestanding, 14308c2ecf20Sopenharmony_ci const struct drm_connector *connector, 14318c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci int i; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 14368c2ecf20Sopenharmony_ci switch (str[i]) { 14378c2ecf20Sopenharmony_ci case 'i': 14388c2ecf20Sopenharmony_ci if (freestanding) 14398c2ecf20Sopenharmony_ci return -EINVAL; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci mode->interlace = true; 14428c2ecf20Sopenharmony_ci break; 14438c2ecf20Sopenharmony_ci case 'm': 14448c2ecf20Sopenharmony_ci if (freestanding) 14458c2ecf20Sopenharmony_ci return -EINVAL; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci mode->margins = true; 14488c2ecf20Sopenharmony_ci break; 14498c2ecf20Sopenharmony_ci case 'D': 14508c2ecf20Sopenharmony_ci if (mode->force != DRM_FORCE_UNSPECIFIED) 14518c2ecf20Sopenharmony_ci return -EINVAL; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && 14548c2ecf20Sopenharmony_ci (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) 14558c2ecf20Sopenharmony_ci mode->force = DRM_FORCE_ON; 14568c2ecf20Sopenharmony_ci else 14578c2ecf20Sopenharmony_ci mode->force = DRM_FORCE_ON_DIGITAL; 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci case 'd': 14608c2ecf20Sopenharmony_ci if (mode->force != DRM_FORCE_UNSPECIFIED) 14618c2ecf20Sopenharmony_ci return -EINVAL; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci mode->force = DRM_FORCE_OFF; 14648c2ecf20Sopenharmony_ci break; 14658c2ecf20Sopenharmony_ci case 'e': 14668c2ecf20Sopenharmony_ci if (mode->force != DRM_FORCE_UNSPECIFIED) 14678c2ecf20Sopenharmony_ci return -EINVAL; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci mode->force = DRM_FORCE_ON; 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci default: 14728c2ecf20Sopenharmony_ci return -EINVAL; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci return 0; 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, 14808c2ecf20Sopenharmony_ci bool extras, 14818c2ecf20Sopenharmony_ci const struct drm_connector *connector, 14828c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci const char *str_start = str; 14858c2ecf20Sopenharmony_ci bool rb = false, cvt = false; 14868c2ecf20Sopenharmony_ci int xres = 0, yres = 0; 14878c2ecf20Sopenharmony_ci int remaining, i; 14888c2ecf20Sopenharmony_ci char *end_ptr; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci xres = simple_strtol(str, &end_ptr, 10); 14918c2ecf20Sopenharmony_ci if (end_ptr == str) 14928c2ecf20Sopenharmony_ci return -EINVAL; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci if (end_ptr[0] != 'x') 14958c2ecf20Sopenharmony_ci return -EINVAL; 14968c2ecf20Sopenharmony_ci end_ptr++; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci str = end_ptr; 14998c2ecf20Sopenharmony_ci yres = simple_strtol(str, &end_ptr, 10); 15008c2ecf20Sopenharmony_ci if (end_ptr == str) 15018c2ecf20Sopenharmony_ci return -EINVAL; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci remaining = length - (end_ptr - str_start); 15048c2ecf20Sopenharmony_ci if (remaining < 0) 15058c2ecf20Sopenharmony_ci return -EINVAL; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci for (i = 0; i < remaining; i++) { 15088c2ecf20Sopenharmony_ci switch (end_ptr[i]) { 15098c2ecf20Sopenharmony_ci case 'M': 15108c2ecf20Sopenharmony_ci cvt = true; 15118c2ecf20Sopenharmony_ci break; 15128c2ecf20Sopenharmony_ci case 'R': 15138c2ecf20Sopenharmony_ci rb = true; 15148c2ecf20Sopenharmony_ci break; 15158c2ecf20Sopenharmony_ci default: 15168c2ecf20Sopenharmony_ci /* 15178c2ecf20Sopenharmony_ci * Try to pass that to our extras parsing 15188c2ecf20Sopenharmony_ci * function to handle the case where the 15198c2ecf20Sopenharmony_ci * extras are directly after the resolution 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_ci if (extras) { 15228c2ecf20Sopenharmony_ci int ret = drm_mode_parse_cmdline_extra(end_ptr + i, 15238c2ecf20Sopenharmony_ci 1, 15248c2ecf20Sopenharmony_ci false, 15258c2ecf20Sopenharmony_ci connector, 15268c2ecf20Sopenharmony_ci mode); 15278c2ecf20Sopenharmony_ci if (ret) 15288c2ecf20Sopenharmony_ci return ret; 15298c2ecf20Sopenharmony_ci } else { 15308c2ecf20Sopenharmony_ci return -EINVAL; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci mode->xres = xres; 15368c2ecf20Sopenharmony_ci mode->yres = yres; 15378c2ecf20Sopenharmony_ci mode->cvt = cvt; 15388c2ecf20Sopenharmony_ci mode->rb = rb; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return 0; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci const char *value; 15468c2ecf20Sopenharmony_ci char *endp; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci /* 15498c2ecf20Sopenharmony_ci * delim must point to the '=', otherwise it is a syntax error and 15508c2ecf20Sopenharmony_ci * if delim points to the terminating zero, then delim + 1 wil point 15518c2ecf20Sopenharmony_ci * past the end of the string. 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci if (*delim != '=') 15548c2ecf20Sopenharmony_ci return -EINVAL; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci value = delim + 1; 15578c2ecf20Sopenharmony_ci *int_ret = simple_strtol(value, &endp, 10); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* Make sure we have parsed something */ 15608c2ecf20Sopenharmony_ci if (endp == value) 15618c2ecf20Sopenharmony_ci return -EINVAL; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci return 0; 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_cistatic int drm_mode_parse_panel_orientation(const char *delim, 15678c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci const char *value; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (*delim != '=') 15728c2ecf20Sopenharmony_ci return -EINVAL; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci value = delim + 1; 15758c2ecf20Sopenharmony_ci delim = strchr(value, ','); 15768c2ecf20Sopenharmony_ci if (!delim) 15778c2ecf20Sopenharmony_ci delim = value + strlen(value); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (!strncmp(value, "normal", delim - value)) 15808c2ecf20Sopenharmony_ci mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; 15818c2ecf20Sopenharmony_ci else if (!strncmp(value, "upside_down", delim - value)) 15828c2ecf20Sopenharmony_ci mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; 15838c2ecf20Sopenharmony_ci else if (!strncmp(value, "left_side_up", delim - value)) 15848c2ecf20Sopenharmony_ci mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; 15858c2ecf20Sopenharmony_ci else if (!strncmp(value, "right_side_up", delim - value)) 15868c2ecf20Sopenharmony_ci mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; 15878c2ecf20Sopenharmony_ci else 15888c2ecf20Sopenharmony_ci return -EINVAL; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return 0; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic int drm_mode_parse_cmdline_options(const char *str, 15948c2ecf20Sopenharmony_ci bool freestanding, 15958c2ecf20Sopenharmony_ci const struct drm_connector *connector, 15968c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci unsigned int deg, margin, rotation = 0; 15998c2ecf20Sopenharmony_ci const char *delim, *option, *sep; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci option = str; 16028c2ecf20Sopenharmony_ci do { 16038c2ecf20Sopenharmony_ci delim = strchr(option, '='); 16048c2ecf20Sopenharmony_ci if (!delim) { 16058c2ecf20Sopenharmony_ci delim = strchr(option, ','); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (!delim) 16088c2ecf20Sopenharmony_ci delim = option + strlen(option); 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (!strncmp(option, "rotate", delim - option)) { 16128c2ecf20Sopenharmony_ci if (drm_mode_parse_cmdline_int(delim, °)) 16138c2ecf20Sopenharmony_ci return -EINVAL; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci switch (deg) { 16168c2ecf20Sopenharmony_ci case 0: 16178c2ecf20Sopenharmony_ci rotation |= DRM_MODE_ROTATE_0; 16188c2ecf20Sopenharmony_ci break; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci case 90: 16218c2ecf20Sopenharmony_ci rotation |= DRM_MODE_ROTATE_90; 16228c2ecf20Sopenharmony_ci break; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci case 180: 16258c2ecf20Sopenharmony_ci rotation |= DRM_MODE_ROTATE_180; 16268c2ecf20Sopenharmony_ci break; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci case 270: 16298c2ecf20Sopenharmony_ci rotation |= DRM_MODE_ROTATE_270; 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci default: 16338c2ecf20Sopenharmony_ci return -EINVAL; 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci } else if (!strncmp(option, "reflect_x", delim - option)) { 16368c2ecf20Sopenharmony_ci rotation |= DRM_MODE_REFLECT_X; 16378c2ecf20Sopenharmony_ci } else if (!strncmp(option, "reflect_y", delim - option)) { 16388c2ecf20Sopenharmony_ci rotation |= DRM_MODE_REFLECT_Y; 16398c2ecf20Sopenharmony_ci } else if (!strncmp(option, "margin_right", delim - option)) { 16408c2ecf20Sopenharmony_ci if (drm_mode_parse_cmdline_int(delim, &margin)) 16418c2ecf20Sopenharmony_ci return -EINVAL; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci mode->tv_margins.right = margin; 16448c2ecf20Sopenharmony_ci } else if (!strncmp(option, "margin_left", delim - option)) { 16458c2ecf20Sopenharmony_ci if (drm_mode_parse_cmdline_int(delim, &margin)) 16468c2ecf20Sopenharmony_ci return -EINVAL; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci mode->tv_margins.left = margin; 16498c2ecf20Sopenharmony_ci } else if (!strncmp(option, "margin_top", delim - option)) { 16508c2ecf20Sopenharmony_ci if (drm_mode_parse_cmdline_int(delim, &margin)) 16518c2ecf20Sopenharmony_ci return -EINVAL; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci mode->tv_margins.top = margin; 16548c2ecf20Sopenharmony_ci } else if (!strncmp(option, "margin_bottom", delim - option)) { 16558c2ecf20Sopenharmony_ci if (drm_mode_parse_cmdline_int(delim, &margin)) 16568c2ecf20Sopenharmony_ci return -EINVAL; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci mode->tv_margins.bottom = margin; 16598c2ecf20Sopenharmony_ci } else if (!strncmp(option, "panel_orientation", delim - option)) { 16608c2ecf20Sopenharmony_ci if (drm_mode_parse_panel_orientation(delim, mode)) 16618c2ecf20Sopenharmony_ci return -EINVAL; 16628c2ecf20Sopenharmony_ci } else { 16638c2ecf20Sopenharmony_ci return -EINVAL; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci sep = strchr(delim, ','); 16668c2ecf20Sopenharmony_ci option = sep + 1; 16678c2ecf20Sopenharmony_ci } while (sep); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (rotation && freestanding) 16708c2ecf20Sopenharmony_ci return -EINVAL; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (!(rotation & DRM_MODE_ROTATE_MASK)) 16738c2ecf20Sopenharmony_ci rotation |= DRM_MODE_ROTATE_0; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* Make sure there is exactly one rotation defined */ 16768c2ecf20Sopenharmony_ci if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)) 16778c2ecf20Sopenharmony_ci return -EINVAL; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci mode->rotation_reflection = rotation; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci return 0; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic const char * const drm_named_modes_whitelist[] = { 16858c2ecf20Sopenharmony_ci "NTSC", 16868c2ecf20Sopenharmony_ci "PAL", 16878c2ecf20Sopenharmony_ci}; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci/** 16908c2ecf20Sopenharmony_ci * drm_mode_parse_command_line_for_connector - parse command line modeline for connector 16918c2ecf20Sopenharmony_ci * @mode_option: optional per connector mode option 16928c2ecf20Sopenharmony_ci * @connector: connector to parse modeline for 16938c2ecf20Sopenharmony_ci * @mode: preallocated drm_cmdline_mode structure to fill out 16948c2ecf20Sopenharmony_ci * 16958c2ecf20Sopenharmony_ci * This parses @mode_option command line modeline for modes and options to 16968c2ecf20Sopenharmony_ci * configure the connector. If @mode_option is NULL the default command line 16978c2ecf20Sopenharmony_ci * modeline in fb_mode_option will be parsed instead. 16988c2ecf20Sopenharmony_ci * 16998c2ecf20Sopenharmony_ci * This uses the same parameters as the fb modedb.c, except for an extra 17008c2ecf20Sopenharmony_ci * force-enable, force-enable-digital and force-disable bit at the end:: 17018c2ecf20Sopenharmony_ci * 17028c2ecf20Sopenharmony_ci * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] 17038c2ecf20Sopenharmony_ci * 17048c2ecf20Sopenharmony_ci * Additionals options can be provided following the mode, using a comma to 17058c2ecf20Sopenharmony_ci * separate each option. Valid options can be found in 17068c2ecf20Sopenharmony_ci * Documentation/fb/modedb.rst. 17078c2ecf20Sopenharmony_ci * 17088c2ecf20Sopenharmony_ci * The intermediate drm_cmdline_mode structure is required to store additional 17098c2ecf20Sopenharmony_ci * options from the command line modline like the force-enable/disable flag. 17108c2ecf20Sopenharmony_ci * 17118c2ecf20Sopenharmony_ci * Returns: 17128c2ecf20Sopenharmony_ci * True if a valid modeline has been parsed, false otherwise. 17138c2ecf20Sopenharmony_ci */ 17148c2ecf20Sopenharmony_cibool drm_mode_parse_command_line_for_connector(const char *mode_option, 17158c2ecf20Sopenharmony_ci const struct drm_connector *connector, 17168c2ecf20Sopenharmony_ci struct drm_cmdline_mode *mode) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci const char *name; 17198c2ecf20Sopenharmony_ci bool freestanding = false, parse_extras = false; 17208c2ecf20Sopenharmony_ci unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; 17218c2ecf20Sopenharmony_ci unsigned int mode_end = 0; 17228c2ecf20Sopenharmony_ci const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; 17238c2ecf20Sopenharmony_ci const char *options_ptr = NULL; 17248c2ecf20Sopenharmony_ci char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; 17258c2ecf20Sopenharmony_ci int i, len, ret; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci memset(mode, 0, sizeof(*mode)); 17288c2ecf20Sopenharmony_ci mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (!mode_option) 17318c2ecf20Sopenharmony_ci return false; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci name = mode_option; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci /* Try to locate the bpp and refresh specifiers, if any */ 17368c2ecf20Sopenharmony_ci bpp_ptr = strchr(name, '-'); 17378c2ecf20Sopenharmony_ci if (bpp_ptr) 17388c2ecf20Sopenharmony_ci bpp_off = bpp_ptr - name; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci refresh_ptr = strchr(name, '@'); 17418c2ecf20Sopenharmony_ci if (refresh_ptr) 17428c2ecf20Sopenharmony_ci refresh_off = refresh_ptr - name; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci /* Locate the start of named options */ 17458c2ecf20Sopenharmony_ci options_ptr = strchr(name, ','); 17468c2ecf20Sopenharmony_ci if (options_ptr) 17478c2ecf20Sopenharmony_ci options_off = options_ptr - name; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* Locate the end of the name / resolution, and parse it */ 17508c2ecf20Sopenharmony_ci if (bpp_ptr) { 17518c2ecf20Sopenharmony_ci mode_end = bpp_off; 17528c2ecf20Sopenharmony_ci } else if (refresh_ptr) { 17538c2ecf20Sopenharmony_ci mode_end = refresh_off; 17548c2ecf20Sopenharmony_ci } else if (options_ptr) { 17558c2ecf20Sopenharmony_ci mode_end = options_off; 17568c2ecf20Sopenharmony_ci parse_extras = true; 17578c2ecf20Sopenharmony_ci } else { 17588c2ecf20Sopenharmony_ci mode_end = strlen(name); 17598c2ecf20Sopenharmony_ci parse_extras = true; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* First check for a named mode */ 17638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) { 17648c2ecf20Sopenharmony_ci ret = str_has_prefix(name, drm_named_modes_whitelist[i]); 17658c2ecf20Sopenharmony_ci if (ret == mode_end) { 17668c2ecf20Sopenharmony_ci if (refresh_ptr) 17678c2ecf20Sopenharmony_ci return false; /* named + refresh is invalid */ 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci strcpy(mode->name, drm_named_modes_whitelist[i]); 17708c2ecf20Sopenharmony_ci mode->specified = true; 17718c2ecf20Sopenharmony_ci break; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* No named mode? Check for a normal mode argument, e.g. 1024x768 */ 17768c2ecf20Sopenharmony_ci if (!mode->specified && isdigit(name[0])) { 17778c2ecf20Sopenharmony_ci ret = drm_mode_parse_cmdline_res_mode(name, mode_end, 17788c2ecf20Sopenharmony_ci parse_extras, 17798c2ecf20Sopenharmony_ci connector, 17808c2ecf20Sopenharmony_ci mode); 17818c2ecf20Sopenharmony_ci if (ret) 17828c2ecf20Sopenharmony_ci return false; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci mode->specified = true; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* No mode? Check for freestanding extras and/or options */ 17888c2ecf20Sopenharmony_ci if (!mode->specified) { 17898c2ecf20Sopenharmony_ci unsigned int len = strlen(mode_option); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (bpp_ptr || refresh_ptr) 17928c2ecf20Sopenharmony_ci return false; /* syntax error */ 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci if (len == 1 || (len >= 2 && mode_option[1] == ',')) 17958c2ecf20Sopenharmony_ci extra_ptr = mode_option; 17968c2ecf20Sopenharmony_ci else 17978c2ecf20Sopenharmony_ci options_ptr = mode_option - 1; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci freestanding = true; 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (bpp_ptr) { 18038c2ecf20Sopenharmony_ci ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode); 18048c2ecf20Sopenharmony_ci if (ret) 18058c2ecf20Sopenharmony_ci return false; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci mode->bpp_specified = true; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (refresh_ptr) { 18118c2ecf20Sopenharmony_ci ret = drm_mode_parse_cmdline_refresh(refresh_ptr, 18128c2ecf20Sopenharmony_ci &refresh_end_ptr, mode); 18138c2ecf20Sopenharmony_ci if (ret) 18148c2ecf20Sopenharmony_ci return false; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci mode->refresh_specified = true; 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci /* 18208c2ecf20Sopenharmony_ci * Locate the end of the bpp / refresh, and parse the extras 18218c2ecf20Sopenharmony_ci * if relevant 18228c2ecf20Sopenharmony_ci */ 18238c2ecf20Sopenharmony_ci if (bpp_ptr && refresh_ptr) 18248c2ecf20Sopenharmony_ci extra_ptr = max(bpp_end_ptr, refresh_end_ptr); 18258c2ecf20Sopenharmony_ci else if (bpp_ptr) 18268c2ecf20Sopenharmony_ci extra_ptr = bpp_end_ptr; 18278c2ecf20Sopenharmony_ci else if (refresh_ptr) 18288c2ecf20Sopenharmony_ci extra_ptr = refresh_end_ptr; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (extra_ptr) { 18318c2ecf20Sopenharmony_ci if (options_ptr) 18328c2ecf20Sopenharmony_ci len = options_ptr - extra_ptr; 18338c2ecf20Sopenharmony_ci else 18348c2ecf20Sopenharmony_ci len = strlen(extra_ptr); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding, 18378c2ecf20Sopenharmony_ci connector, mode); 18388c2ecf20Sopenharmony_ci if (ret) 18398c2ecf20Sopenharmony_ci return false; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (options_ptr) { 18438c2ecf20Sopenharmony_ci ret = drm_mode_parse_cmdline_options(options_ptr + 1, 18448c2ecf20Sopenharmony_ci freestanding, 18458c2ecf20Sopenharmony_ci connector, mode); 18468c2ecf20Sopenharmony_ci if (ret) 18478c2ecf20Sopenharmony_ci return false; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci return true; 18518c2ecf20Sopenharmony_ci} 18528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci/** 18558c2ecf20Sopenharmony_ci * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode 18568c2ecf20Sopenharmony_ci * @dev: DRM device to create the new mode for 18578c2ecf20Sopenharmony_ci * @cmd: input command line modeline 18588c2ecf20Sopenharmony_ci * 18598c2ecf20Sopenharmony_ci * Returns: 18608c2ecf20Sopenharmony_ci * Pointer to converted mode on success, NULL on error. 18618c2ecf20Sopenharmony_ci */ 18628c2ecf20Sopenharmony_cistruct drm_display_mode * 18638c2ecf20Sopenharmony_cidrm_mode_create_from_cmdline_mode(struct drm_device *dev, 18648c2ecf20Sopenharmony_ci struct drm_cmdline_mode *cmd) 18658c2ecf20Sopenharmony_ci{ 18668c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (cmd->cvt) 18698c2ecf20Sopenharmony_ci mode = drm_cvt_mode(dev, 18708c2ecf20Sopenharmony_ci cmd->xres, cmd->yres, 18718c2ecf20Sopenharmony_ci cmd->refresh_specified ? cmd->refresh : 60, 18728c2ecf20Sopenharmony_ci cmd->rb, cmd->interlace, 18738c2ecf20Sopenharmony_ci cmd->margins); 18748c2ecf20Sopenharmony_ci else 18758c2ecf20Sopenharmony_ci mode = drm_gtf_mode(dev, 18768c2ecf20Sopenharmony_ci cmd->xres, cmd->yres, 18778c2ecf20Sopenharmony_ci cmd->refresh_specified ? cmd->refresh : 60, 18788c2ecf20Sopenharmony_ci cmd->interlace, 18798c2ecf20Sopenharmony_ci cmd->margins); 18808c2ecf20Sopenharmony_ci if (!mode) 18818c2ecf20Sopenharmony_ci return NULL; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci mode->type |= DRM_MODE_TYPE_USERDEF; 18848c2ecf20Sopenharmony_ci /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ 18858c2ecf20Sopenharmony_ci if (cmd->xres == 1366) 18868c2ecf20Sopenharmony_ci drm_mode_fixup_1366x768(mode); 18878c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 18888c2ecf20Sopenharmony_ci return mode; 18898c2ecf20Sopenharmony_ci} 18908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci/** 18938c2ecf20Sopenharmony_ci * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo 18948c2ecf20Sopenharmony_ci * @out: drm_mode_modeinfo struct to return to the user 18958c2ecf20Sopenharmony_ci * @in: drm_display_mode to use 18968c2ecf20Sopenharmony_ci * 18978c2ecf20Sopenharmony_ci * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 18988c2ecf20Sopenharmony_ci * the user. 18998c2ecf20Sopenharmony_ci */ 19008c2ecf20Sopenharmony_civoid drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, 19018c2ecf20Sopenharmony_ci const struct drm_display_mode *in) 19028c2ecf20Sopenharmony_ci{ 19038c2ecf20Sopenharmony_ci out->clock = in->clock; 19048c2ecf20Sopenharmony_ci out->hdisplay = in->hdisplay; 19058c2ecf20Sopenharmony_ci out->hsync_start = in->hsync_start; 19068c2ecf20Sopenharmony_ci out->hsync_end = in->hsync_end; 19078c2ecf20Sopenharmony_ci out->htotal = in->htotal; 19088c2ecf20Sopenharmony_ci out->hskew = in->hskew; 19098c2ecf20Sopenharmony_ci out->vdisplay = in->vdisplay; 19108c2ecf20Sopenharmony_ci out->vsync_start = in->vsync_start; 19118c2ecf20Sopenharmony_ci out->vsync_end = in->vsync_end; 19128c2ecf20Sopenharmony_ci out->vtotal = in->vtotal; 19138c2ecf20Sopenharmony_ci out->vscan = in->vscan; 19148c2ecf20Sopenharmony_ci out->vrefresh = drm_mode_vrefresh(in); 19158c2ecf20Sopenharmony_ci out->flags = in->flags; 19168c2ecf20Sopenharmony_ci out->type = in->type; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci switch (in->picture_aspect_ratio) { 19198c2ecf20Sopenharmony_ci case HDMI_PICTURE_ASPECT_4_3: 19208c2ecf20Sopenharmony_ci out->flags |= DRM_MODE_FLAG_PIC_AR_4_3; 19218c2ecf20Sopenharmony_ci break; 19228c2ecf20Sopenharmony_ci case HDMI_PICTURE_ASPECT_16_9: 19238c2ecf20Sopenharmony_ci out->flags |= DRM_MODE_FLAG_PIC_AR_16_9; 19248c2ecf20Sopenharmony_ci break; 19258c2ecf20Sopenharmony_ci case HDMI_PICTURE_ASPECT_64_27: 19268c2ecf20Sopenharmony_ci out->flags |= DRM_MODE_FLAG_PIC_AR_64_27; 19278c2ecf20Sopenharmony_ci break; 19288c2ecf20Sopenharmony_ci case HDMI_PICTURE_ASPECT_256_135: 19298c2ecf20Sopenharmony_ci out->flags |= DRM_MODE_FLAG_PIC_AR_256_135; 19308c2ecf20Sopenharmony_ci break; 19318c2ecf20Sopenharmony_ci default: 19328c2ecf20Sopenharmony_ci WARN(1, "Invalid aspect ratio (0%x) on mode\n", 19338c2ecf20Sopenharmony_ci in->picture_aspect_ratio); 19348c2ecf20Sopenharmony_ci fallthrough; 19358c2ecf20Sopenharmony_ci case HDMI_PICTURE_ASPECT_NONE: 19368c2ecf20Sopenharmony_ci out->flags |= DRM_MODE_FLAG_PIC_AR_NONE; 19378c2ecf20Sopenharmony_ci break; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 19418c2ecf20Sopenharmony_ci out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci/** 19458c2ecf20Sopenharmony_ci * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode 19468c2ecf20Sopenharmony_ci * @dev: drm device 19478c2ecf20Sopenharmony_ci * @out: drm_display_mode to return to the user 19488c2ecf20Sopenharmony_ci * @in: drm_mode_modeinfo to use 19498c2ecf20Sopenharmony_ci * 19508c2ecf20Sopenharmony_ci * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 19518c2ecf20Sopenharmony_ci * the caller. 19528c2ecf20Sopenharmony_ci * 19538c2ecf20Sopenharmony_ci * Returns: 19548c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure. 19558c2ecf20Sopenharmony_ci */ 19568c2ecf20Sopenharmony_ciint drm_mode_convert_umode(struct drm_device *dev, 19578c2ecf20Sopenharmony_ci struct drm_display_mode *out, 19588c2ecf20Sopenharmony_ci const struct drm_mode_modeinfo *in) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 19618c2ecf20Sopenharmony_ci return -ERANGE; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci out->clock = in->clock; 19648c2ecf20Sopenharmony_ci out->hdisplay = in->hdisplay; 19658c2ecf20Sopenharmony_ci out->hsync_start = in->hsync_start; 19668c2ecf20Sopenharmony_ci out->hsync_end = in->hsync_end; 19678c2ecf20Sopenharmony_ci out->htotal = in->htotal; 19688c2ecf20Sopenharmony_ci out->hskew = in->hskew; 19698c2ecf20Sopenharmony_ci out->vdisplay = in->vdisplay; 19708c2ecf20Sopenharmony_ci out->vsync_start = in->vsync_start; 19718c2ecf20Sopenharmony_ci out->vsync_end = in->vsync_end; 19728c2ecf20Sopenharmony_ci out->vtotal = in->vtotal; 19738c2ecf20Sopenharmony_ci out->vscan = in->vscan; 19748c2ecf20Sopenharmony_ci out->flags = in->flags; 19758c2ecf20Sopenharmony_ci /* 19768c2ecf20Sopenharmony_ci * Old xf86-video-vmware (possibly others too) used to 19778c2ecf20Sopenharmony_ci * leave 'type' unititialized. Just ignore any bits we 19788c2ecf20Sopenharmony_ci * don't like. It's a just hint after all, and more 19798c2ecf20Sopenharmony_ci * useful for the kernel->userspace direction anyway. 19808c2ecf20Sopenharmony_ci */ 19818c2ecf20Sopenharmony_ci out->type = in->type & DRM_MODE_TYPE_ALL; 19828c2ecf20Sopenharmony_ci strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 19838c2ecf20Sopenharmony_ci out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* Clearing picture aspect ratio bits from out flags, 19868c2ecf20Sopenharmony_ci * as the aspect-ratio information is not stored in 19878c2ecf20Sopenharmony_ci * flags for kernel-mode, but in picture_aspect_ratio. 19888c2ecf20Sopenharmony_ci */ 19898c2ecf20Sopenharmony_ci out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) { 19928c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_PIC_AR_4_3: 19938c2ecf20Sopenharmony_ci out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3; 19948c2ecf20Sopenharmony_ci break; 19958c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_PIC_AR_16_9: 19968c2ecf20Sopenharmony_ci out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; 19978c2ecf20Sopenharmony_ci break; 19988c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_PIC_AR_64_27: 19998c2ecf20Sopenharmony_ci out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27; 20008c2ecf20Sopenharmony_ci break; 20018c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_PIC_AR_256_135: 20028c2ecf20Sopenharmony_ci out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; 20038c2ecf20Sopenharmony_ci break; 20048c2ecf20Sopenharmony_ci case DRM_MODE_FLAG_PIC_AR_NONE: 20058c2ecf20Sopenharmony_ci out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; 20068c2ecf20Sopenharmony_ci break; 20078c2ecf20Sopenharmony_ci default: 20088c2ecf20Sopenharmony_ci return -EINVAL; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci out->status = drm_mode_validate_driver(dev, out); 20128c2ecf20Sopenharmony_ci if (out->status != MODE_OK) 20138c2ecf20Sopenharmony_ci return -EINVAL; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(out, CRTC_INTERLACE_HALVE_V); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci return 0; 20188c2ecf20Sopenharmony_ci} 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci/** 20218c2ecf20Sopenharmony_ci * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 20228c2ecf20Sopenharmony_ci * output format 20238c2ecf20Sopenharmony_ci * 20248c2ecf20Sopenharmony_ci * @display: display under action 20258c2ecf20Sopenharmony_ci * @mode: video mode to be tested. 20268c2ecf20Sopenharmony_ci * 20278c2ecf20Sopenharmony_ci * Returns: 20288c2ecf20Sopenharmony_ci * true if the mode can be supported in YCBCR420 format 20298c2ecf20Sopenharmony_ci * false if not. 20308c2ecf20Sopenharmony_ci */ 20318c2ecf20Sopenharmony_cibool drm_mode_is_420_only(const struct drm_display_info *display, 20328c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci u8 vic = drm_match_cea_mode(mode); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci return test_bit(vic, display->hdmi.y420_vdb_modes); 20378c2ecf20Sopenharmony_ci} 20388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_is_420_only); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci/** 20418c2ecf20Sopenharmony_ci * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 20428c2ecf20Sopenharmony_ci * output format also (along with RGB/YCBCR444/422) 20438c2ecf20Sopenharmony_ci * 20448c2ecf20Sopenharmony_ci * @display: display under action. 20458c2ecf20Sopenharmony_ci * @mode: video mode to be tested. 20468c2ecf20Sopenharmony_ci * 20478c2ecf20Sopenharmony_ci * Returns: 20488c2ecf20Sopenharmony_ci * true if the mode can be support YCBCR420 format 20498c2ecf20Sopenharmony_ci * false if not. 20508c2ecf20Sopenharmony_ci */ 20518c2ecf20Sopenharmony_cibool drm_mode_is_420_also(const struct drm_display_info *display, 20528c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci u8 vic = drm_match_cea_mode(mode); 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci return test_bit(vic, display->hdmi.y420_cmdb_modes); 20578c2ecf20Sopenharmony_ci} 20588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_is_420_also); 20598c2ecf20Sopenharmony_ci/** 20608c2ecf20Sopenharmony_ci * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 20618c2ecf20Sopenharmony_ci * output format 20628c2ecf20Sopenharmony_ci * 20638c2ecf20Sopenharmony_ci * @display: display under action. 20648c2ecf20Sopenharmony_ci * @mode: video mode to be tested. 20658c2ecf20Sopenharmony_ci * 20668c2ecf20Sopenharmony_ci * Returns: 20678c2ecf20Sopenharmony_ci * true if the mode can be supported in YCBCR420 format 20688c2ecf20Sopenharmony_ci * false if not. 20698c2ecf20Sopenharmony_ci */ 20708c2ecf20Sopenharmony_cibool drm_mode_is_420(const struct drm_display_info *display, 20718c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci return drm_mode_is_420_only(display, mode) || 20748c2ecf20Sopenharmony_ci drm_mode_is_420_also(display, mode); 20758c2ecf20Sopenharmony_ci} 20768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_mode_is_420); 2077