/** * drm_mode_debug_printmodeline - print a mode to dmesg * @mode: mode to print * * Describe @mode using DRM_DEBUG.
*/ void drm_mode_debug_printmodeline(conststruct drm_display_mode *mode)
{
DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
}
EXPORT_SYMBOL(drm_mode_debug_printmodeline);
/** * drm_mode_create - create a new display mode * @dev: DRM device * * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it * and return it. * * Returns: * Pointer to new mode on success, NULL on error.
*/ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
{ struct drm_display_mode *nmode;
nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); if (!nmode) return NULL;
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
/** * drm_mode_destroy - remove a mode * @dev: DRM device * @mode: mode to remove * * Release @mode's unique ID, then free it @mode structure itself using kfree.
*/ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
{ if (!mode) return;
kfree(mode);
}
EXPORT_SYMBOL(drm_mode_destroy);
/** * drm_mode_probed_add - add a mode to a connector's probed_mode list * @connector: connector the new mode * @mode: mode data * * Add @mode to @connector's probed_mode list for later use. This list should * then in a second step get filtered and all the modes actually supported by * the hardware moved to the @connector's modes list.
*/ void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode)
{
WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
/* * 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)
/* * 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)),
};
/* * 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_err(dev, "Invalid horizontal active area duration: %uns (min: %u, max %u)\n",
hact_duration_ns, params->hact_ns.min, params->hact_ns.max); return -EINVAL;
}
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_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 modes. Where * this is DRM_MODE_TV_MODE_MONOCHROME, a 625-line mode will be created. * * 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, unsignedlong pixel_clock_hz, unsignedint hdisplay, unsignedint 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:
fallthrough; case DRM_MODE_TV_MODE_MONOCHROME:
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;
/** * drm_cvt_mode -create a modeline based on the CVT algorithm * @dev: drm device * @hdisplay: hdisplay size * @vdisplay: vdisplay size * @vrefresh: vrefresh rate * @reduced: whether to use reduced blanking * @interlaced: whether to compute an interlaced mode * @margins: whether to add margins (borders) * * This function is called to generate the modeline based on CVT algorithm * according to the hdisplay, vdisplay, vrefresh. * It is based from the VESA(TM) Coordinated Video Timing Generator by * Graham Loveridge April 9, 2003 available at * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls * * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. * What I have done is to translate it by using integer calculation. * * Returns: * The modeline based on the CVT algorithm stored in a drm_display_mode object. * The display mode object is allocated with drm_mode_create(). Returns NULL * when no mode could be allocated.
*/ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool reduced, bool interlaced, bool margins)
{ #define HV_FACTOR 1000 /* 1) top/bottom margin size (% of height) - default: 1.8, */ #define CVT_MARGIN_PERCENTAGE 18 /* 2) character cell horizontal granularity (pixels) - default 8 */ #define CVT_H_GRANULARITY 8 /* 3) Minimum vertical porch (lines) - default 3 */ #define CVT_MIN_V_PORCH 3 /* 4) Minimum number of vertical back porch lines - default 6 */ #define CVT_MIN_V_BPORCH 6 /* Pixel Clock step (kHz) */ #define CVT_CLOCK_STEP 250 struct drm_display_mode *drm_mode; unsignedint vfieldrate, hperiod; int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; int interlace;
u64 tmp;
if (!hdisplay || !vdisplay) return NULL;
/* allocate the drm_display_mode structure. If failure, we will * return directly
*/
drm_mode = drm_mode_create(dev); if (!drm_mode) return NULL;
/* the CVT default refresh rate is 60Hz */ if (!vrefresh)
vrefresh = 60;
/* the required field fresh rate */ if (interlaced)
vfieldrate = vrefresh * 2; else
vfieldrate = vrefresh;
tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / hperiod + 1; /* 9. Find number of lines in sync + backporch */ if (tmp1 < (vsync + CVT_MIN_V_PORCH))
vsyncandback_porch = vsync + CVT_MIN_V_PORCH; else
vsyncandback_porch = tmp1; /* 10. Find number of lines in back porch */
vback_porch = vsyncandback_porch - vsync;
drm_mode->vtotal = vdisplay_rnd + 2 * vmargin +
vsyncandback_porch + CVT_MIN_V_PORCH; /* 5) Definition of Horizontal blanking time limitation */ /* Gradient (%/kHz) - default 600 */ #define CVT_M_FACTOR 600 /* Offset (%) - default 40 */ #define CVT_C_FACTOR 40 /* Blanking time scaling factor - default 128 */ #define CVT_K_FACTOR 128 /* Scaling factor weighting - default 20 */ #define CVT_J_FACTOR 20 #define CVT_M_PRIME (CVT_M_FACTOR * CVT_K_FACTOR / 256) #define CVT_C_PRIME ((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
CVT_J_FACTOR) /* 12. Find ideal blanking duty cycle from formula */
hblank_percentage = CVT_C_PRIME * HV_FACTOR - CVT_M_PRIME *
hperiod / 1000; /* 13. Blanking time */ if (hblank_percentage < 20 * HV_FACTOR)
hblank_percentage = 20 * HV_FACTOR;
hblank = drm_mode->hdisplay * hblank_percentage /
(100 * HV_FACTOR - hblank_percentage);
hblank -= hblank % (2 * CVT_H_GRANULARITY); /* 14. find the total pixels per line */
drm_mode->htotal = drm_mode->hdisplay + hblank;
drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2;
drm_mode->hsync_start = drm_mode->hsync_end -
(drm_mode->htotal * CVT_HSYNC_PERCENTAGE) / 100;
drm_mode->hsync_start += CVT_H_GRANULARITY -
drm_mode->hsync_start % CVT_H_GRANULARITY; /* fill the Vsync values */
drm_mode->vsync_start = drm_mode->vdisplay + CVT_MIN_V_PORCH;
drm_mode->vsync_end = drm_mode->vsync_start + vsync;
} else { /* Reduced blanking */ /* Minimum vertical blanking interval time (µs)- default 460 */ #define CVT_RB_MIN_VBLANK 460 /* Fixed number of clocks for horizontal sync */ #define CVT_RB_H_SYNC 32 /* Fixed number of clocks for horizontal blanking */ #define CVT_RB_H_BLANK 160 /* Fixed number of lines for vertical front porch - default 3*/ #define CVT_RB_VFPORCH 3 int vbilines; int tmp1, tmp2; /* 8. Estimate Horizontal period. */
tmp1 = HV_FACTOR * 1000000 -
CVT_RB_MIN_VBLANK * HV_FACTOR * vfieldrate;
tmp2 = vdisplay_rnd + 2 * vmargin;
hperiod = tmp1 / (tmp2 * vfieldrate); /* 9. Find number of lines in vertical blanking */
vbilines = CVT_RB_MIN_VBLANK * HV_FACTOR / hperiod + 1; /* 10. Check if vertical blanking is sufficient */ if (vbilines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH))
vbilines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; /* 11. Find total number of lines in vertical field */
drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + vbilines; /* 12. Find total number of pixels in a line */
drm_mode->htotal = drm_mode->hdisplay + CVT_RB_H_BLANK; /* Fill in HSync values */
drm_mode->hsync_end = drm_mode->hdisplay + CVT_RB_H_BLANK / 2;
drm_mode->hsync_start = drm_mode->hsync_end - CVT_RB_H_SYNC; /* Fill in VSync values */
drm_mode->vsync_start = drm_mode->vdisplay + CVT_RB_VFPORCH;
drm_mode->vsync_end = drm_mode->vsync_start + vsync;
} /* 15/13. Find pixel clock frequency (kHz for xf86) */
tmp = drm_mode->htotal; /* perform intermediate calcs in u64 */
tmp *= HV_FACTOR * 1000;
do_div(tmp, hperiod);
tmp -= drm_mode->clock % CVT_CLOCK_STEP;
drm_mode->clock = tmp; /* 18/16. Find actual vertical frame frequency */ /* ignore - just set the mode flag for interlaced */ if (interlaced) {
drm_mode->vtotal *= 2;
drm_mode->flags |= DRM_MODE_FLAG_INTERLACE;
} /* Fill the mode line name */
drm_mode_set_name(drm_mode); if (reduced)
drm_mode->flags |= (DRM_MODE_FLAG_PHSYNC |
DRM_MODE_FLAG_NVSYNC); else
drm_mode->flags |= (DRM_MODE_FLAG_PVSYNC |
DRM_MODE_FLAG_NHSYNC);
return drm_mode;
}
EXPORT_SYMBOL(drm_cvt_mode);
/** * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm * @dev: drm device * @hdisplay: hdisplay size * @vdisplay: vdisplay size * @vrefresh: vrefresh rate. * @interlaced: whether to compute an interlaced mode * @margins: desired margin (borders) size * @GTF_M: extended GTF formula parameters * @GTF_2C: extended GTF formula parameters * @GTF_K: extended GTF formula parameters * @GTF_2J: extended GTF formula parameters * * GTF feature blocks specify C and J in multiples of 0.5, so we pass them * in here multiplied by two. For a C of 40, pass in 80. * * Returns: * The modeline based on the full GTF algorithm stored in a drm_display_mode object. * The display mode object is allocated with drm_mode_create(). Returns NULL * when no mode could be allocated.
*/ struct drm_display_mode *
drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool interlaced, int margins, int GTF_M, int GTF_2C, int GTF_K, int GTF_2J)
{ /* 1) top/bottom margin size (% of height) - default: 1.8, */ #define GTF_MARGIN_PERCENTAGE 18 /* 2) character cell horizontal granularity (pixels) - default 8 */ #define GTF_CELL_GRAN 8 /* 3) Minimum vertical porch (lines) - default 3 */ #define GTF_MIN_V_PORCH 1 /* width of vsync in lines */ #define V_SYNC_RQD 3 /* width of hsync as % of total line */ #define H_SYNC_PERCENT 8 /* min time of vsync + back porch (microsec) */ #define MIN_VSYNC_PLUS_BP 550 /* C' and M' are part of the Blanking Duty Cycle computation */ #define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) #define GTF_M_PRIME (GTF_K * GTF_M / 256) struct drm_display_mode *drm_mode; unsignedint hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; int top_margin, bottom_margin; int interlace; unsignedint hfreq_est; int vsync_plus_bp, __maybe_unused vback_porch; unsignedint vtotal_lines, __maybe_unused vfieldrate_est; unsignedint __maybe_unused hperiod; unsignedint vfield_rate, __maybe_unused vframe_rate; int left_margin, right_margin; unsignedint total_active_pixels, ideal_duty_cycle; unsignedint hblank, total_pixels, pixel_freq; int hsync, hfront_porch, vodd_front_porch_lines; unsignedint tmp1, tmp2;
if (!hdisplay || !vdisplay) return NULL;
drm_mode = drm_mode_create(dev); if (!drm_mode) return NULL;
/* 1. In order to give correct results, the number of horizontal * pixels requested is first processed to ensure that it is divisible * by the character size, by rounding it to the nearest character * cell boundary:
*/
hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN;
hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN;
/* 2. If interlace is requested, the number of vertical lines assumed * by the calculation must be halved, as the computation calculates * the number of vertical lines per field.
*/ if (interlaced)
vdisplay_rnd = vdisplay / 2; else
vdisplay_rnd = vdisplay;
/* 3. Find the frame rate required: */ if (interlaced)
vfieldrate_rqd = vrefresh * 2; else
vfieldrate_rqd = vrefresh;
/* 4. Find number of lines in Top margin: */
top_margin = 0; if (margins)
top_margin = (vdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) /
1000; /* 5. Find number of lines in bottom margin: */
bottom_margin = top_margin;
/* 6. If interlace is required, then set variable interlace: */ if (interlaced)
interlace = 1; else
interlace = 0;
/* 8. Find the number of lines in V sync + back porch */ /* [V SYNC+BP] = RINT(([MIN VSYNC+BP] * hfreq_est / 1000000)) */
vsync_plus_bp = MIN_VSYNC_PLUS_BP * hfreq_est / 1000;
vsync_plus_bp = (vsync_plus_bp + 500) / 1000; /* 9. Find the number of lines in V back porch alone: */
vback_porch = vsync_plus_bp - V_SYNC_RQD; /* 10. Find the total number of lines in Vertical field period: */
vtotal_lines = vdisplay_rnd + top_margin + bottom_margin +
vsync_plus_bp + GTF_MIN_V_PORCH; /* 11. Estimate the Vertical field frequency: */
vfieldrate_est = hfreq_est / vtotal_lines; /* 12. Find the actual horizontal period: */
hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines);
/* 13. Find the actual Vertical field frequency: */
vfield_rate = hfreq_est / vtotal_lines; /* 14. Find the Vertical frame frequency: */ if (interlaced)
vframe_rate = vfield_rate / 2; else
vframe_rate = vfield_rate; /* 15. Find number of pixels in left margin: */ if (margins)
left_margin = (hdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) /
1000; else
left_margin = 0;
/* 16.Find number of pixels in right margin: */
right_margin = left_margin; /* 17.Find total number of active pixels in image and left and right */
total_active_pixels = hdisplay_rnd + left_margin + right_margin; /* 18.Find the ideal blanking duty cycle from blanking duty cycle */
ideal_duty_cycle = GTF_C_PRIME * 1000 -
(GTF_M_PRIME * 1000000 / hfreq_est); /* 19.Find the number of pixels in the blanking time to the nearest
* double character cell: */
hblank = total_active_pixels * ideal_duty_cycle /
(100000 - ideal_duty_cycle);
hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN);
hblank = hblank * 2 * GTF_CELL_GRAN; /* 20.Find total number of pixels: */
total_pixels = total_active_pixels + hblank; /* 21.Find pixel clock frequency: */
pixel_freq = total_pixels * hfreq_est / 1000; /* Stage 1 computations are now complete; I should really pass * the results to another function and do the Stage 2 computations, * but I only need a few more values so I'll just append the
* computations here for now */ /* 17. Find the number of pixels in the horizontal sync period: */
hsync = H_SYNC_PERCENT * total_pixels / 100;
hsync = (hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN;
hsync = hsync * GTF_CELL_GRAN; /* 18. Find the number of pixels in horizontal front porch period */
hfront_porch = hblank / 2 - hsync; /* 36. Find the number of lines in the odd front porch period: */
vodd_front_porch_lines = GTF_MIN_V_PORCH ;
/** * drm_gtf_mode - create the modeline based on the GTF algorithm * @dev: drm device * @hdisplay: hdisplay size * @vdisplay: vdisplay size * @vrefresh: vrefresh rate. * @interlaced: whether to compute an interlaced mode * @margins: desired margin (borders) size * * return the modeline based on GTF algorithm * * This function is to create the modeline based on the GTF algorithm. * Generalized Timing Formula is derived from: * * GTF Spreadsheet by Andy Morrish (1/5/97) * available at https://www.vesa.org * * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. * What I have done is to translate it by using integer calculation. * I also refer to the function of fb_get_mode in the file of * drivers/video/fbmon.c * * Standard GTF parameters:: * * M = 600 * C = 40 * K = 128 * J = 20 * * Returns: * The modeline based on the GTF algorithm stored in a drm_display_mode object. * The display mode object is allocated with drm_mode_create(). Returns NULL * when no mode could be allocated.
*/ struct drm_display_mode *
drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool interlaced, int margins)
{ return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh,
interlaced, margins,
600, 40 * 2, 128, 20 * 2);
}
EXPORT_SYMBOL(drm_gtf_mode);
#ifdef CONFIG_VIDEOMODE_HELPERS /** * drm_display_mode_from_videomode - fill in @dmode using @vm, * @vm: videomode structure to use as source * @dmode: drm_display_mode structure to use as destination * * Fills out @dmode using the display mode specified in @vm.
*/ void drm_display_mode_from_videomode(conststruct videomode *vm, struct drm_display_mode *dmode)
{
dmode->hdisplay = vm->hactive;
dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
dmode->htotal = dmode->hsync_end + vm->hback_porch;
/** * drm_display_mode_to_videomode - fill in @vm using @dmode, * @dmode: drm_display_mode structure to use as source * @vm: videomode structure to use as destination * * Fills out @vm using the display mode specified in @dmode.
*/ void drm_display_mode_to_videomode(conststruct drm_display_mode *dmode, struct videomode *vm)
{
vm->hactive = dmode->hdisplay;
vm->hfront_porch = dmode->hsync_start - dmode->hdisplay;
vm->hsync_len = dmode->hsync_end - dmode->hsync_start;
vm->hback_porch = dmode->htotal - dmode->hsync_end;
/** * drm_bus_flags_from_videomode - extract information about pixelclk and * DE polarity from videomode and store it in a separate variable * @vm: videomode structure to use * @bus_flags: information about pixelclk, sync and DE polarity will be stored * here * * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS * found in @vm
*/ void drm_bus_flags_from_videomode(conststruct videomode *vm, u32 *bus_flags)
{
*bus_flags = 0; if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
*bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
*bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
*bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE)
*bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
if (vm->flags & DISPLAY_FLAGS_DE_LOW)
*bus_flags |= DRM_BUS_FLAG_DE_LOW; if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
*bus_flags |= DRM_BUS_FLAG_DE_HIGH;
}
EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode);
#ifdef CONFIG_OF /** * of_get_drm_display_mode - get a drm_display_mode from devicetree * @np: device_node with the timing specification * @dmode: will be set to the return value * @bus_flags: information about pixelclk, sync and DE polarity * @index: index into the list of display timings in devicetree * * This function is expensive and should only be used, if only one mode is to be * read from DT. To get multiple modes start with of_get_display_timings and * work with that instead. * * Returns: * 0 on success, a negative errno code when no of videomode node was found.
*/ int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode, u32 *bus_flags, int index)
{ struct videomode vm; int ret;
ret = of_get_videomode(np, &vm, index); if (ret) return ret;
drm_display_mode_from_videomode(&vm, dmode); if (bus_flags)
drm_bus_flags_from_videomode(&vm, bus_flags);
/** * of_get_drm_panel_display_mode - get a panel-timing drm_display_mode from devicetree * @np: device_node with the panel-timing specification * @dmode: will be set to the return value * @bus_flags: information about pixelclk, sync and DE polarity * * The mandatory Device Tree properties width-mm and height-mm * are read and set on the display mode. * * Returns: * Zero on success, negative error code on failure.
*/ int of_get_drm_panel_display_mode(struct device_node *np, struct drm_display_mode *dmode, u32 *bus_flags)
{
u32 width_mm = 0, height_mm = 0; struct display_timing timing; struct videomode vm; int ret;
ret = of_get_display_timing(np, "panel-timing", &timing); if (ret) return ret;
videomode_from_timing(&timing, &vm);
memset(dmode, 0, sizeof(*dmode));
drm_display_mode_from_videomode(&vm, dmode); if (bus_flags)
drm_bus_flags_from_videomode(&vm, bus_flags);
ret = of_property_read_u32(np, "width-mm", &width_mm); if (ret) return ret;
ret = of_property_read_u32(np, "height-mm", &height_mm); if (ret) return ret;
/** * drm_mode_set_name - set the name on a mode * @mode: name will be set in this mode * * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay> * with an optional 'i' suffix for interlaced modes.
*/ void drm_mode_set_name(struct drm_display_mode *mode)
{ bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
/** * drm_mode_vrefresh - get the vrefresh of a mode * @mode: mode * * Returns: * @modes's vrefresh rate in Hz, rounded to the nearest integer.
*/ int drm_mode_vrefresh(conststruct drm_display_mode *mode)
{ unsignedint num = 1, den = 1;
if (mode->htotal == 0 || mode->vtotal == 0) return 0;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
num *= 2; if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
den *= 2; if (mode->vscan > 1)
den *= mode->vscan;
if (check_mul_overflow(mode->clock, num, &num)) return 0;
if (check_mul_overflow(mode->htotal * mode->vtotal, den, &den)) return 0;
/** * drm_mode_get_hv_timing - Fetches hdisplay/vdisplay for given mode * @mode: mode to query * @hdisplay: hdisplay value to fill in * @vdisplay: vdisplay value to fill in * * The vdisplay value will be doubled if the specified mode is a stereo mode of * the appropriate layout.
*/ void drm_mode_get_hv_timing(conststruct drm_display_mode *mode, int *hdisplay, int *vdisplay)
{ struct drm_display_mode adjusted;
/** * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters * @p: mode * @adjust_flags: a combination of adjustment flags * * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. * * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of * interlaced modes. * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for * buffers containing two eyes (only adjust the timings when needed, eg. for * "frame packing" or "side by side full"). * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* * be performed for doublescan and vscan > 1 modes respectively.
*/ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
{ if (!p) return;
/** * drm_mode_copy - copy the mode * @dst: mode to overwrite * @src: mode to copy * * Copy an existing mode into another mode, preserving the * list head of the destination mode.
*/ void drm_mode_copy(struct drm_display_mode *dst, conststruct drm_display_mode *src)
{ struct list_head head = dst->head;
/** * drm_mode_init - initialize the mode from another mode * @dst: mode to overwrite * @src: mode to copy * * Copy an existing mode into another mode, zeroing the * list head of the destination mode. Typically used * to guarantee the list head is not left with stack * garbage in on-stack modes.
*/ void drm_mode_init(struct drm_display_mode *dst, conststruct drm_display_mode *src)
{
memset(dst, 0, sizeof(*dst));
drm_mode_copy(dst, src);
}
EXPORT_SYMBOL(drm_mode_init);
/** * drm_mode_duplicate - allocate and duplicate an existing mode * @dev: drm_device to allocate the duplicated mode for * @mode: mode to duplicate * * Just allocate a new mode, copy the existing mode into it, and return * a pointer to it. Used to create new instances of established modes. * * Returns: * Pointer to duplicated mode on success, NULL on error.
*/ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, conststruct drm_display_mode *mode)
{ struct drm_display_mode *nmode;
nmode = drm_mode_create(dev); if (!nmode) return NULL;
/** * drm_mode_match - test modes for (partial) equality * @mode1: first mode * @mode2: second mode * @match_flags: which parts need to match (DRM_MODE_MATCH_*) * * Check to see if @mode1 and @mode2 are equivalent. * * Returns: * True if the modes are (partially) equal, false otherwise.
*/ bool drm_mode_match(conststruct drm_display_mode *mode1, conststruct drm_display_mode *mode2, unsignedint match_flags)
{ if (!mode1 && !mode2) returntrue;
if (!mode1 || !mode2) returnfalse;
if (match_flags & DRM_MODE_MATCH_TIMINGS &&
!drm_mode_match_timings(mode1, mode2)) returnfalse;
if (match_flags & DRM_MODE_MATCH_CLOCK &&
!drm_mode_match_clock(mode1, mode2)) returnfalse;
if (match_flags & DRM_MODE_MATCH_FLAGS &&
!drm_mode_match_flags(mode1, mode2)) returnfalse;
if (match_flags & DRM_MODE_MATCH_3D_FLAGS &&
!drm_mode_match_3d_flags(mode1, mode2)) returnfalse;
if (match_flags & DRM_MODE_MATCH_ASPECT_RATIO &&
!drm_mode_match_aspect_ratio(mode1, mode2)) returnfalse;
returntrue;
}
EXPORT_SYMBOL(drm_mode_match);
/** * drm_mode_equal - test modes for equality * @mode1: first mode * @mode2: second mode * * Check to see if @mode1 and @mode2 are equivalent. * * Returns: * True if the modes are equal, false otherwise.
*/ bool drm_mode_equal(conststruct drm_display_mode *mode1, conststruct drm_display_mode *mode2)
{ return drm_mode_match(mode1, mode2,
DRM_MODE_MATCH_TIMINGS |
DRM_MODE_MATCH_CLOCK |
DRM_MODE_MATCH_FLAGS |
DRM_MODE_MATCH_3D_FLAGS|
DRM_MODE_MATCH_ASPECT_RATIO);
}
EXPORT_SYMBOL(drm_mode_equal);
/** * drm_mode_equal_no_clocks - test modes for equality * @mode1: first mode * @mode2: second mode * * Check to see if @mode1 and @mode2 are equivalent, but * don't check the pixel clocks. * * Returns: * True if the modes are equal, false otherwise.
*/ bool drm_mode_equal_no_clocks(conststruct drm_display_mode *mode1, conststruct drm_display_mode *mode2)
{ return drm_mode_match(mode1, mode2,
DRM_MODE_MATCH_TIMINGS |
DRM_MODE_MATCH_FLAGS |
DRM_MODE_MATCH_3D_FLAGS);
}
EXPORT_SYMBOL(drm_mode_equal_no_clocks);
/** * drm_mode_equal_no_clocks_no_stereo - test modes for equality * @mode1: first mode * @mode2: second mode * * Check to see if @mode1 and @mode2 are equivalent, but * don't check the pixel clocks nor the stereo layout. * * Returns: * True if the modes are equal, false otherwise.
*/ bool drm_mode_equal_no_clocks_no_stereo(conststruct drm_display_mode *mode1, conststruct drm_display_mode *mode2)
{ return drm_mode_match(mode1, mode2,
DRM_MODE_MATCH_TIMINGS |
DRM_MODE_MATCH_FLAGS);
}
EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
/** * drm_mode_validate_driver - make sure the mode is somewhat sane * @dev: drm device * @mode: mode to check * * First do basic validation on the mode, and then allow the driver * to check for device/driver specific limitations via the optional * &drm_mode_config_helper_funcs.mode_valid hook. * * Returns: * The mode status
*/ enum drm_mode_status
drm_mode_validate_driver(struct drm_device *dev, conststruct drm_display_mode *mode)
{ enum drm_mode_status status;
status = drm_mode_validate_basic(mode); if (status != MODE_OK) return status;
if (dev->mode_config.funcs->mode_valid) return dev->mode_config.funcs->mode_valid(dev, mode); else return MODE_OK;
}
EXPORT_SYMBOL(drm_mode_validate_driver);
/** * drm_mode_validate_size - make sure modes adhere to size constraints * @mode: mode to check * @maxX: maximum width * @maxY: maximum height * * This function is a helper which can be used to validate modes against size * limitations of the DRM device/connector. If a mode is too big its status * member is updated with the appropriate validation failure code. The list * itself is not changed. * * Returns: * The mode status
*/ enum drm_mode_status
drm_mode_validate_size(conststruct drm_display_mode *mode, int maxX, int maxY)
{ if (maxX > 0 && mode->hdisplay > maxX) return MODE_VIRTUAL_X;
if (maxY > 0 && mode->vdisplay > maxY) return MODE_VIRTUAL_Y;
/** * drm_mode_validate_ycbcr420 - add 'ycbcr420-only' modes only when allowed * @mode: mode to check * @connector: drm connector under action * * This function is a helper which can be used to filter out any YCBCR420 * only mode, when the source doesn't support it. * * Returns: * The mode status
*/ enum drm_mode_status
drm_mode_validate_ycbcr420(conststruct drm_display_mode *mode, struct drm_connector *connector)
{ if (!connector->ycbcr_420_allowed &&
drm_mode_is_420_only(&connector->display_info, mode)) return MODE_NO_420;
constchar *drm_get_mode_status_name(enum drm_mode_status status)
{ int index = status + 3;
if (WARN_ON(index < 0 || index >= ARRAY_SIZE(drm_mode_status_names))) return"";
return drm_mode_status_names[index];
}
/** * drm_mode_prune_invalid - remove invalid modes from mode list * @dev: DRM device * @mode_list: list of modes to check * @verbose: be verbose about it * * This helper function can be used to prune a display mode list after * validation has been completed. All modes whose status is not MODE_OK will be * removed from the list, and if @verbose the status code and mode name is also * printed to dmesg.
*/ void drm_mode_prune_invalid(struct drm_device *dev, struct list_head *mode_list, bool verbose)
{ struct drm_display_mode *mode, *t;
/** * drm_mode_compare - compare modes for favorability * @priv: unused * @lh_a: list_head for first mode * @lh_b: list_head for second mode * * Compare two modes, given by @lh_a and @lh_b, returning a value indicating * which is better. * * Returns: * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or * positive if @lh_b is better than @lh_a.
*/ staticint drm_mode_compare(void *priv, conststruct list_head *lh_a, conststruct list_head *lh_b)
{ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); int diff;
diff = drm_mode_vrefresh(b) - drm_mode_vrefresh(a); if (diff) return diff;
diff = b->clock - a->clock; return diff;
}
/** * drm_mode_sort - sort mode list * @mode_list: list of drm_display_mode structures to sort * * Sort @mode_list by favorability, moving good modes to the head of the list.
*/ void drm_mode_sort(struct list_head *mode_list)
{
list_sort(NULL, mode_list, drm_mode_compare);
}
EXPORT_SYMBOL(drm_mode_sort);
/** * drm_connector_list_update - update the mode list for the connector * @connector: the connector to update * * This moves the modes from the @connector probed_modes list * to the actual mode list. It compares the probed mode against the current * list and only adds different/new modes. * * This is just a helper functions doesn't validate any modes itself and also * doesn't prune any invalid modes. Callers need to do that themselves.
*/ void drm_connector_list_update(struct drm_connector *connector)
{ struct drm_display_mode *pmode, *pt;
/* go through current modes checking for the new probed mode */
list_for_each_entry(mode, &connector->modes, head) { if (!drm_mode_equal(pmode, mode)) continue;
found_it = true;
/* * If the old matching mode is stale (ie. left over * from a previous probe) just replace it outright. * Otherwise just merge the type bits between all * equal probed modes. * * If two probed modes are considered equal, pick the * actual timings from the one that's marked as * preferred (in case the match isn't 100%). If * multiple or zero preferred modes are present, favor * the mode added to the probed_modes list first.
*/ if (mode->status == MODE_STALE) {
drm_mode_copy(mode, pmode);
} elseif ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 &&
(pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) {
pmode->type |= mode->type;
drm_mode_copy(mode, pmode);
} else {
mode->type |= pmode->type;
}
for (i = 0; i < remaining; i++) { switch (end_ptr[i]) { case'M':
cvt = true; break; case'R':
rb = true; break; default: /* * Try to pass that to our extras parsing * function to handle the case where the * extras are directly after the resolution
*/ if (extras) { int ret = drm_mode_parse_cmdline_extra(end_ptr + i,
1, false,
connector,
mode); if (ret) return ret;
} else { return -EINVAL;
}
}
}
/* * delim must point to the '=', otherwise it is a syntax error and * if delim points to the terminating zero, then delim + 1 will point * past the end of the string.
*/ if (*delim != '=') return -EINVAL;
value = delim + 1;
*int_ret = simple_strtol(value, &endp, 10);
/* Make sure we have parsed something */ if (endp == value) return -EINVAL;