sync with OpenBSD -current
This commit is contained in:
parent
ee68147dcd
commit
1cefe29c7e
1651 changed files with 283292 additions and 68089 deletions
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue