sync with OpenBSD -current

This commit is contained in:
purplerain 2024-01-18 08:29:14 +00:00
parent ee68147dcd
commit 1cefe29c7e
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
1651 changed files with 283292 additions and 68089 deletions

View file

@ -31,10 +31,11 @@
*/
#include <linux/ctype.h>
#include <linux/export.h>
#include <linux/fb.h> /* for KHZ2PICOS() */
#include <linux/list.h>
#include <linux/list_sort.h>
#include <linux/export.h>
#include <linux/fb.h>
#include <linux/of.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
@ -116,6 +117,482 @@ void drm_mode_probed_add(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_mode_probed_add);
enum drm_mode_analog {
DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */
DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */
};
/*
* The timings come from:
* - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html
* - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html
* - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm
*/
#define NTSC_LINE_DURATION_NS 63556U
#define NTSC_LINES_NUMBER 525
#define NTSC_HBLK_DURATION_TYP_NS 10900U
#define NTSC_HBLK_DURATION_MIN_NS (NTSC_HBLK_DURATION_TYP_NS - 200)
#define NTSC_HBLK_DURATION_MAX_NS (NTSC_HBLK_DURATION_TYP_NS + 200)
#define NTSC_HACT_DURATION_TYP_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS)
#define NTSC_HACT_DURATION_MIN_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS)
#define NTSC_HACT_DURATION_MAX_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS)
#define NTSC_HFP_DURATION_TYP_NS 1500
#define NTSC_HFP_DURATION_MIN_NS 1270
#define NTSC_HFP_DURATION_MAX_NS 2220
#define NTSC_HSLEN_DURATION_TYP_NS 4700
#define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100)
#define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100)
#define NTSC_HBP_DURATION_TYP_NS 4700
/*
* I couldn't find the actual tolerance for the back porch, so let's
* just reuse the sync length ones.
*/
#define NTSC_HBP_DURATION_MIN_NS (NTSC_HBP_DURATION_TYP_NS - 100)
#define NTSC_HBP_DURATION_MAX_NS (NTSC_HBP_DURATION_TYP_NS + 100)
#define PAL_LINE_DURATION_NS 64000U
#define PAL_LINES_NUMBER 625
#define PAL_HACT_DURATION_TYP_NS 51950U
#define PAL_HACT_DURATION_MIN_NS (PAL_HACT_DURATION_TYP_NS - 100)
#define PAL_HACT_DURATION_MAX_NS (PAL_HACT_DURATION_TYP_NS + 400)
#define PAL_HBLK_DURATION_TYP_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS)
#define PAL_HBLK_DURATION_MIN_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS)
#define PAL_HBLK_DURATION_MAX_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS)
#define PAL_HFP_DURATION_TYP_NS 1650
#define PAL_HFP_DURATION_MIN_NS (PAL_HFP_DURATION_TYP_NS - 100)
#define PAL_HFP_DURATION_MAX_NS (PAL_HFP_DURATION_TYP_NS + 400)
#define PAL_HSLEN_DURATION_TYP_NS 4700
#define PAL_HSLEN_DURATION_MIN_NS (PAL_HSLEN_DURATION_TYP_NS - 200)
#define PAL_HSLEN_DURATION_MAX_NS (PAL_HSLEN_DURATION_TYP_NS + 200)
#define PAL_HBP_DURATION_TYP_NS 5700
#define PAL_HBP_DURATION_MIN_NS (PAL_HBP_DURATION_TYP_NS - 200)
#define PAL_HBP_DURATION_MAX_NS (PAL_HBP_DURATION_TYP_NS + 200)
struct analog_param_field {
unsigned int even, odd;
};
#define PARAM_FIELD(_odd, _even) \
{ .even = _even, .odd = _odd }
struct analog_param_range {
unsigned int min, typ, max;
};
#define PARAM_RANGE(_min, _typ, _max) \
{ .min = _min, .typ = _typ, .max = _max }
struct analog_parameters {
unsigned int num_lines;
unsigned int line_duration_ns;
struct analog_param_range hact_ns;
struct analog_param_range hfp_ns;
struct analog_param_range hslen_ns;
struct analog_param_range hbp_ns;
struct analog_param_range hblk_ns;
unsigned int bt601_hfp;
struct analog_param_field vfp_lines;
struct analog_param_field vslen_lines;
struct analog_param_field vbp_lines;
};
#define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp, \
_hslen, _hbp, _hblk, _bt601_hfp, _vfp, \
_vslen, _vbp) \
[_mode] = { \
.num_lines = _lines, \
.line_duration_ns = _line_dur, \
.hact_ns = _hact, \
.hfp_ns = _hfp, \
.hslen_ns = _hslen, \
.hbp_ns = _hbp, \
.hblk_ns = _hblk, \
.bt601_hfp = _bt601_hfp, \
.vfp_lines = _vfp, \
.vslen_lines = _vslen, \
.vbp_lines = _vbp, \
}
static const struct analog_parameters tv_modes_parameters[] = {
TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC,
NTSC_LINES_NUMBER,
NTSC_LINE_DURATION_NS,
PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS,
NTSC_HACT_DURATION_TYP_NS,
NTSC_HACT_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS,
NTSC_HFP_DURATION_TYP_NS,
NTSC_HFP_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS,
NTSC_HSLEN_DURATION_TYP_NS,
NTSC_HSLEN_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS,
NTSC_HBP_DURATION_TYP_NS,
NTSC_HBP_DURATION_MAX_NS),
PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS,
NTSC_HBLK_DURATION_TYP_NS,
NTSC_HBLK_DURATION_MAX_NS),
16,
PARAM_FIELD(3, 3),
PARAM_FIELD(3, 3),
PARAM_FIELD(16, 17)),
TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL,
PAL_LINES_NUMBER,
PAL_LINE_DURATION_NS,
PARAM_RANGE(PAL_HACT_DURATION_MIN_NS,
PAL_HACT_DURATION_TYP_NS,
PAL_HACT_DURATION_MAX_NS),
PARAM_RANGE(PAL_HFP_DURATION_MIN_NS,
PAL_HFP_DURATION_TYP_NS,
PAL_HFP_DURATION_MAX_NS),
PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS,
PAL_HSLEN_DURATION_TYP_NS,
PAL_HSLEN_DURATION_MAX_NS),
PARAM_RANGE(PAL_HBP_DURATION_MIN_NS,
PAL_HBP_DURATION_TYP_NS,
PAL_HBP_DURATION_MAX_NS),
PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS,
PAL_HBLK_DURATION_TYP_NS,
PAL_HBLK_DURATION_MAX_NS),
12,
/*
* The front porch is actually 6 short sync
* pulses for the even field, and 5 for the
* odd field. Each sync takes half a life so
* the odd field front porch is shorter by
* half a line.
*
* In progressive, we're supposed to use 6
* pulses, so we're fine there
*/
PARAM_FIELD(3, 2),
/*
* The vsync length is 5 long sync pulses,
* each field taking half a line. We're
* shorter for both fields by half a line.
*
* In progressive, we're supposed to use 5
* pulses, so we're off by half
* a line.
*
* In interlace, we're now off by half a line
* for the even field and one line for the odd
* field.
*/
PARAM_FIELD(3, 3),
/*
* The back porch starts with post-equalizing
* pulses, consisting in 5 short sync pulses
* for the even field, 4 for the odd field. In
* progressive, it's 5 short syncs.
*
* In progressive, we thus have 2.5 lines,
* plus the 0.5 line we were missing
* previously, so we should use 3 lines.
*
* In interlace, the even field is in the
* exact same case than progressive. For the
* odd field, we should be using 2 lines but
* we're one line short, so we'll make up for
* it here by using 3.
*
* The entire blanking area is supposed to
* take 25 lines, so we also need to account
* for the rest of the blanking area that
* can't be in either the front porch or sync
* period.
*/
PARAM_FIELD(19, 20)),
};
static int fill_analog_mode(struct drm_device *dev,
struct drm_display_mode *mode,
const struct analog_parameters *params,
unsigned long pixel_clock_hz,
unsigned int hactive,
unsigned int vactive,
bool interlace)
{
unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz;
unsigned int htotal, vtotal;
unsigned int max_hact, hact_duration_ns;
unsigned int hblk, hblk_duration_ns;
unsigned int hfp, hfp_duration_ns;
unsigned int hslen, hslen_duration_ns;
unsigned int hbp, hbp_duration_ns;
unsigned int porches, porches_duration_ns;
unsigned int vfp, vfp_min;
unsigned int vbp, vbp_min;
unsigned int vslen;
bool bt601 = false;
int porches_rem;
u64 result;
drm_dbg_kms(dev,
"Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n",
hactive, vactive,
interlace ? 'i' : 'p',
params->num_lines,
pixel_clock_hz / 1000);
max_hact = params->hact_ns.max / pixel_duration_ns;
if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) {
drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n");
bt601 = true;
}
/*
* Our pixel duration is going to be round down by the division,
* so rounding up is probably going to introduce even more
* deviation.
*/
result = (u64)params->line_duration_ns * pixel_clock_hz;
do_div(result, NSEC_PER_SEC);
htotal = result;
drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal);
hact_duration_ns = hactive * pixel_duration_ns;
if (!bt601 &&
(hact_duration_ns < params->hact_ns.min ||
hact_duration_ns > params->hact_ns.max)) {
DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n",
hact_duration_ns, params->hact_ns.min, params->hact_ns.max);
return -EINVAL;
}
hblk = htotal - hactive;
drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk);
hblk_duration_ns = hblk * pixel_duration_ns;
if (!bt601 &&
(hblk_duration_ns < params->hblk_ns.min ||
hblk_duration_ns > params->hblk_ns.max)) {
DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n",
hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max);
return -EINVAL;
}
hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns);
drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen);
hslen_duration_ns = hslen * pixel_duration_ns;
if (!bt601 &&
(hslen_duration_ns < params->hslen_ns.min ||
hslen_duration_ns > params->hslen_ns.max)) {
DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n",
hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max);
return -EINVAL;
}
porches = hblk - hslen;
drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches);
porches_duration_ns = porches * pixel_duration_ns;
if (!bt601 &&
(porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) ||
porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) {
DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns);
return -EINVAL;
}
if (bt601) {
hfp = params->bt601_hfp;
} else {
unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min,
pixel_duration_ns);
unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min,
pixel_duration_ns);
int porches_rem = porches - hfp_min - hbp_min;
hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2);
}
drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp);
hfp_duration_ns = hfp * pixel_duration_ns;
if (!bt601 &&
(hfp_duration_ns < params->hfp_ns.min ||
hfp_duration_ns > params->hfp_ns.max)) {
DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n",
hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max);
return -EINVAL;
}
hbp = porches - hfp;
drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp);
hbp_duration_ns = hbp * pixel_duration_ns;
if (!bt601 &&
(hbp_duration_ns < params->hbp_ns.min ||
hbp_duration_ns > params->hbp_ns.max)) {
DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n",
hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max);
return -EINVAL;
}
if (htotal != (hactive + hfp + hslen + hbp))
return -EINVAL;
mode->clock = pixel_clock_hz / 1000;
mode->hdisplay = hactive;
mode->hsync_start = mode->hdisplay + hfp;
mode->hsync_end = mode->hsync_start + hslen;
mode->htotal = mode->hsync_end + hbp;
if (interlace) {
vfp_min = params->vfp_lines.even + params->vfp_lines.odd;
vbp_min = params->vbp_lines.even + params->vbp_lines.odd;
vslen = params->vslen_lines.even + params->vslen_lines.odd;
} else {
/*
* By convention, NTSC (aka 525/60) systems start with
* the even field, but PAL (aka 625/50) systems start
* with the odd one.
*
* PAL systems also have asymmetric timings between the
* even and odd field, while NTSC is symmetric.
*
* Moreover, if we want to create a progressive mode for
* PAL, we need to use the odd field timings.
*
* Since odd == even for NTSC, we can just use the odd
* one all the time to simplify the code a bit.
*/
vfp_min = params->vfp_lines.odd;
vbp_min = params->vbp_lines.odd;
vslen = params->vslen_lines.odd;
}
drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen);
porches = params->num_lines - vactive - vslen;
drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches);
porches_rem = porches - vfp_min - vbp_min;
vfp = vfp_min + (porches_rem / 2);
drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp);
vbp = porches - vfp;
drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp);
vtotal = vactive + vfp + vslen + vbp;
if (params->num_lines != vtotal) {
DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n",
vtotal, params->num_lines);
return -EINVAL;
}
mode->vdisplay = vactive;
mode->vsync_start = mode->vdisplay + vfp;
mode->vsync_end = mode->vsync_start + vslen;
mode->vtotal = mode->vsync_end + vbp;
if (mode->vtotal != params->num_lines)
return -EINVAL;
mode->type = DRM_MODE_TYPE_DRIVER;
mode->flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC;
if (interlace)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
drm_mode_set_name(mode);
drm_dbg_kms(dev, "Generated mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
return 0;
}
/**
* drm_analog_tv_mode - create a display mode for an analog TV
* @dev: drm device
* @tv_mode: TV Mode standard to create a mode for. See DRM_MODE_TV_MODE_*.
* @pixel_clock_hz: Pixel Clock Frequency, in Hertz
* @hdisplay: hdisplay size
* @vdisplay: vdisplay size
* @interlace: whether to compute an interlaced mode
*
* This function creates a struct drm_display_mode instance suited for
* an analog TV output, for one of the usual analog TV mode.
*
* Note that @hdisplay is larger than the usual constraints for the PAL
* and NTSC timings, and we'll choose to ignore most timings constraints
* to reach those resolutions.
*
* Returns:
*
* A pointer to the mode, allocated with drm_mode_create(). Returns NULL
* on error.
*/
struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev,
enum drm_connector_tv_mode tv_mode,
unsigned long pixel_clock_hz,
unsigned int hdisplay,
unsigned int vdisplay,
bool interlace)
{
struct drm_display_mode *mode;
enum drm_mode_analog analog;
int ret;
switch (tv_mode) {
case DRM_MODE_TV_MODE_NTSC:
fallthrough;
case DRM_MODE_TV_MODE_NTSC_443:
fallthrough;
case DRM_MODE_TV_MODE_NTSC_J:
fallthrough;
case DRM_MODE_TV_MODE_PAL_M:
analog = DRM_MODE_ANALOG_NTSC;
break;
case DRM_MODE_TV_MODE_PAL:
fallthrough;
case DRM_MODE_TV_MODE_PAL_N:
fallthrough;
case DRM_MODE_TV_MODE_SECAM:
analog = DRM_MODE_ANALOG_PAL;
break;
default:
return NULL;
}
mode = drm_mode_create(dev);
if (!mode)
return NULL;
ret = fill_analog_mode(dev, mode,
&tv_modes_parameters[analog],
pixel_clock_hz, hdisplay, vdisplay, interlace);
if (ret)
goto err_free_mode;
return mode;
err_free_mode:
drm_mode_destroy(dev, mode);
return NULL;
}
EXPORT_SYMBOL(drm_analog_tv_mode);
/**
* drm_cvt_mode -create a modeline based on the CVT algorithm
* @dev: drm device
@ -1661,6 +2138,30 @@ static int drm_mode_parse_panel_orientation(const char *delim,
return 0;
}
static int drm_mode_parse_tv_mode(const char *delim,
struct drm_cmdline_mode *mode)
{
const char *value;
int ret;
if (*delim != '=')
return -EINVAL;
value = delim + 1;
delim = strchr(value, ',');
if (!delim)
delim = value + strlen(value);
ret = drm_get_tv_mode_from_name(value, delim - value);
if (ret < 0)
return ret;
mode->tv_mode_specified = true;
mode->tv_mode = ret;
return 0;
}
static int drm_mode_parse_cmdline_options(const char *str,
bool freestanding,
const struct drm_connector *connector,
@ -1730,6 +2231,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
} else if (!strncmp(option, "panel_orientation", delim - option)) {
if (drm_mode_parse_panel_orientation(delim, mode))
return -EINVAL;
} else if (!strncmp(option, "tv_mode", delim - option)) {
if (drm_mode_parse_tv_mode(delim, mode))
return -EINVAL;
} else {
return -EINVAL;
}
@ -1754,11 +2258,88 @@ static int drm_mode_parse_cmdline_options(const char *str,
#endif /* __linux__ */
static const char * const drm_named_modes_whitelist[] = {
"NTSC",
"PAL",
struct drm_named_mode {
const char *name;
unsigned int pixel_clock_khz;
unsigned int xres;
unsigned int yres;
unsigned int flags;
unsigned int tv_mode;
};
#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \
{ \
.name = _name, \
.pixel_clock_khz = _pclk, \
.xres = _x, \
.yres = _y, \
.flags = _flags, \
.tv_mode = _mode, \
}
static const struct drm_named_mode drm_named_modes[] = {
NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC),
NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J),
NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL),
NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M),
};
#ifdef __linux__
static int drm_mode_parse_cmdline_named_mode(const char *name,
unsigned int name_end,
struct drm_cmdline_mode *cmdline_mode)
{
unsigned int i;
if (!name_end)
return 0;
/* If the name starts with a digit, it's not a named mode */
if (isdigit(name[0]))
return 0;
/*
* If there's an equal sign in the name, the command-line
* contains only an option and no mode.
*/
if (strnchr(name, name_end, '='))
return 0;
/* The connection status extras can be set without a mode. */
if (name_end == 1 &&
(name[0] == 'd' || name[0] == 'D' || name[0] == 'e'))
return 0;
/*
* We're sure we're a named mode at this point, iterate over the
* list of modes we're aware of.
*/
for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {
const struct drm_named_mode *mode = &drm_named_modes[i];
int ret;
ret = str_has_prefix(name, mode->name);
if (ret != name_end)
continue;
strscpy(cmdline_mode->name, mode->name, sizeof(cmdline_mode->name));
cmdline_mode->pixel_clock = mode->pixel_clock_khz;
cmdline_mode->xres = mode->xres;
cmdline_mode->yres = mode->yres;
cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
cmdline_mode->tv_mode = mode->tv_mode;
cmdline_mode->tv_mode_specified = true;
cmdline_mode->specified = true;
return 1;
}
return -EINVAL;
}
#endif /* __linux__ */
/**
* drm_mode_parse_command_line_for_connector - parse command line modeline for connector
* @mode_option: optional per connector mode option
@ -1766,8 +2347,7 @@ static const char * const drm_named_modes_whitelist[] = {
* @mode: preallocated drm_cmdline_mode structure to fill out
*
* This parses @mode_option command line modeline for modes and options to
* configure the connector. If @mode_option is NULL the default command line
* modeline in fb_mode_option will be parsed instead.
* configure the connector.
*
* This uses the same parameters as the fb modedb.c, except for an extra
* force-enable, force-enable-digital and force-disable bit at the end::
@ -1796,7 +2376,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
const char *options_ptr = NULL;
char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
int i, len, ret;
int len, ret;
memset(mode, 0, sizeof(*mode));
mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
@ -1806,19 +2386,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
name = mode_option;
/* Try to locate the bpp and refresh specifiers, if any */
bpp_ptr = strchr(name, '-');
if (bpp_ptr)
bpp_off = bpp_ptr - name;
refresh_ptr = strchr(name, '@');
if (refresh_ptr)
refresh_off = refresh_ptr - name;
/* Locate the start of named options */
options_ptr = strchr(name, ',');
if (options_ptr)
options_off = options_ptr - name;
else
options_off = strlen(name);
/* Try to locate the bpp and refresh specifiers, if any */
bpp_ptr = strnchr(name, options_off, '-');
while (bpp_ptr && !isdigit(bpp_ptr[1]))
bpp_ptr = strnchr(bpp_ptr + 1, options_off, '-');
if (bpp_ptr)
bpp_off = bpp_ptr - name;
refresh_ptr = strnchr(name, options_off, '@');
if (refresh_ptr)
refresh_off = refresh_ptr - name;
/* Locate the end of the name / resolution, and parse it */
if (bpp_ptr) {
@ -1833,18 +2417,19 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
parse_extras = true;
}
/* First check for a named mode */
for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
if (ret == mode_end) {
if (refresh_ptr)
return false; /* named + refresh is invalid */
if (!mode_end)
return false;
strcpy(mode->name, drm_named_modes_whitelist[i]);
mode->specified = true;
break;
}
}
ret = drm_mode_parse_cmdline_named_mode(name, mode_end, mode);
if (ret < 0)
return false;
/*
* Having a mode that starts by a letter (and thus is named) and
* an at-sign (used to specify a refresh rate) is disallowed.
*/
if (ret && refresh_ptr)
return false;
/* No named mode? Check for a normal mode argument, e.g. 1024x768 */
if (!mode->specified && isdigit(name[0])) {
@ -1928,6 +2513,31 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
}
EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
static struct drm_display_mode *drm_named_mode(struct drm_device *dev,
struct drm_cmdline_mode *cmd)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {
const struct drm_named_mode *named_mode = &drm_named_modes[i];
if (strcmp(cmd->name, named_mode->name))
continue;
if (!cmd->tv_mode_specified)
continue;
return drm_analog_tv_mode(dev,
named_mode->tv_mode,
named_mode->pixel_clock_khz * 1000,
named_mode->xres,
named_mode->yres,
named_mode->flags & DRM_MODE_FLAG_INTERLACE);
}
return NULL;
}
/**
* drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode
* @dev: DRM device to create the new mode for
@ -1945,7 +2555,9 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
if (cmd->xres == 0 || cmd->yres == 0)
return NULL;
if (cmd->cvt)
if (strlen(cmd->name))
mode = drm_named_mode(dev, cmd);
else if (cmd->cvt)
mode = drm_cvt_mode(dev,
cmd->xres, cmd->yres,
cmd->refresh_specified ? cmd->refresh : 60,