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, &deg))
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