35,15 → 35,14 |
#include <linux/export.h> |
#include <drm/drmP.h> |
#include <drm/drm_crtc.h> |
#include <drm/drm_modes.h> |
|
#include "drm_crtc_internal.h" |
|
/** |
* drm_mode_debug_printmodeline - debug print a mode |
* @dev: DRM device |
* drm_mode_debug_printmodeline - print a mode to dmesg |
* @mode: mode to print |
* |
* LOCKING: |
* None. |
* |
* Describe @mode using DRM_DEBUG. |
*/ |
void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) |
59,19 → 58,78 |
EXPORT_SYMBOL(drm_mode_debug_printmodeline); |
|
/** |
* drm_cvt_mode -create a modeline based on CVT algorithm |
* 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; |
|
if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { |
kfree(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; |
|
drm_mode_object_put(dev, &mode->base); |
|
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)); |
|
list_add_tail(&mode->head, &connector->probed_modes); |
} |
EXPORT_SYMBOL(drm_mode_probed_add); |
|
/** |
* 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 the GTF calculation is simplified |
* @interlaced:Whether the interlace is supported |
* @reduced: whether to use reduced blanking |
* @interlaced: whether to compute an interlaced mode |
* @margins: whether to add margins (borders) |
* |
* LOCKING: |
* none. |
* |
* return the modeline based on CVT algorithm |
* |
* 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 |
80,12 → 138,17 |
* |
* 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. |
*/ |
#define HV_FACTOR 1000 |
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 */ |
279,23 → 342,25 |
EXPORT_SYMBOL(drm_cvt_mode); |
|
/** |
* drm_gtf_mode_complex - create the modeline based on full GTF algorithm |
* |
* 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 the interlace is supported |
* @margins :desired margin size |
* @GTF_[MCKJ] :extended GTF formula parameters |
* @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 |
* |
* LOCKING. |
* none. |
* |
* return the modeline based on full GTF algorithm. |
* |
* 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, |
465,18 → 530,14 |
EXPORT_SYMBOL(drm_gtf_mode_complex); |
|
/** |
* drm_gtf_mode - create the modeline based on GTF algorithm |
* |
* 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 the interlace is supported |
* @margins :whether the margin is supported |
* @interlaced: whether to compute an interlaced mode |
* @margins: desired margin (borders) size |
* |
* LOCKING. |
* none. |
* |
* return the modeline based on GTF algorithm |
* |
* This function is to create the modeline based on the GTF algorithm. |
494,18 → 555,31 |
* 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 lace, int margins) |
bool interlaced, int margins) |
{ |
return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace, |
margins, 600, 40 * 2, 128, 20 * 2); |
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 |
int drm_display_mode_from_videomode(const struct videomode *vm, |
/** |
* 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(const struct videomode *vm, |
struct drm_display_mode *dmode) |
{ |
dmode->hdisplay = vm->hactive; |
536,8 → 610,6 |
if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) |
dmode->flags |= DRM_MODE_FLAG_DBLCLK; |
drm_mode_set_name(dmode); |
|
return 0; |
} |
EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); |
|
551,6 → 623,9 |
* 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, int index) |
578,10 → 653,8 |
* drm_mode_set_name - set the name on a mode |
* @mode: name will be set in this mode |
* |
* LOCKING: |
* None. |
* |
* Set the name of @mode to a standard format. |
* 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) |
{ |
593,54 → 666,12 |
} |
EXPORT_SYMBOL(drm_mode_set_name); |
|
/** |
* drm_mode_width - get the width of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's width (hdisplay) value. |
* |
* FIXME: is this needed? |
* |
* RETURNS: |
* @mode->hdisplay |
*/ |
int drm_mode_width(const struct drm_display_mode *mode) |
{ |
return mode->hdisplay; |
|
} |
EXPORT_SYMBOL(drm_mode_width); |
|
/** |
* drm_mode_height - get the height of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's height (vdisplay) value. |
* |
* FIXME: is this needed? |
* |
* RETURNS: |
* @mode->vdisplay |
*/ |
int drm_mode_height(const struct drm_display_mode *mode) |
{ |
return mode->vdisplay; |
} |
EXPORT_SYMBOL(drm_mode_height); |
|
/** drm_mode_hsync - get the hsync of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @modes's hsync rate in kHz, rounded to the nearest int. |
* Returns: |
* @modes's hsync rate in kHz, rounded to the nearest integer. Calculates the |
* value first if it is not yet set. |
*/ |
int drm_mode_hsync(const struct drm_display_mode *mode) |
{ |
664,17 → 695,9 |
* drm_mode_vrefresh - get the vrefresh of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's vrefresh rate in Hz or calculate it if necessary. |
* |
* FIXME: why is this needed? shouldn't vrefresh be set already? |
* |
* RETURNS: |
* Vertical refresh rate. It will be the result of actual value plus 0.5. |
* If it is 70.288, it will return 70Hz. |
* If it is 59.6, it will return 60Hz. |
* Returns: |
* @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the |
* value first if it is not yet set. |
*/ |
int drm_mode_vrefresh(const struct drm_display_mode *mode) |
{ |
703,15 → 726,12 |
EXPORT_SYMBOL(drm_mode_vrefresh); |
|
/** |
* drm_mode_set_crtcinfo - set CRTC modesetting parameters |
* drm_mode_set_crtcinfo - set CRTC modesetting timing parameters |
* @p: mode |
* @adjust_flags: a combination of adjustment flags |
* |
* LOCKING: |
* None. |
* Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. |
* |
* Setup the CRTC modesetting 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 |
778,15 → 798,11 |
} |
EXPORT_SYMBOL(drm_mode_set_crtcinfo); |
|
|
/** |
* drm_mode_copy - copy the mode |
* @dst: mode to overwrite |
* @src: mode to copy |
* |
* LOCKING: |
* None. |
* |
* Copy an existing mode into another mode, preserving the object id and |
* list head of the destination mode. |
*/ |
803,13 → 819,14 |
|
/** |
* drm_mode_duplicate - allocate and duplicate an existing mode |
* @m: mode to duplicate |
* @dev: drm_device to allocate the duplicated mode for |
* @mode: mode to duplicate |
* |
* LOCKING: |
* None. |
* |
* 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, |
const struct drm_display_mode *mode) |
831,12 → 848,9 |
* @mode1: first mode |
* @mode2: second mode |
* |
* LOCKING: |
* None. |
* |
* Check to see if @mode1 and @mode2 are equivalent. |
* |
* RETURNS: |
* Returns: |
* True if the modes are equal, false otherwise. |
*/ |
bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) |
862,13 → 876,10 |
* @mode1: first mode |
* @mode2: second mode |
* |
* LOCKING: |
* None. |
* |
* Check to see if @mode1 and @mode2 are equivalent, but |
* don't check the pixel clocks nor the stereo layout. |
* |
* RETURNS: |
* Returns: |
* True if the modes are equal, false otherwise. |
*/ |
bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, |
898,25 → 909,19 |
* @mode_list: list of modes to check |
* @maxX: maximum width |
* @maxY: maximum height |
* @maxPitch: max pitch |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* The DRM device (@dev) has size and pitch limits. Here we validate the |
* modes we probed for @dev against those limits and set their status as |
* necessary. |
* 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 |
* memeber is updated with the appropriate validation failure code. The list |
* itself is not changed. |
*/ |
void drm_mode_validate_size(struct drm_device *dev, |
struct list_head *mode_list, |
int maxX, int maxY, int maxPitch) |
int maxX, int maxY) |
{ |
struct drm_display_mode *mode; |
|
list_for_each_entry(mode, mode_list, head) { |
if (maxPitch > 0 && mode->hdisplay > maxPitch) |
mode->status = MODE_BAD_WIDTH; |
|
if (maxX > 0 && mode->hdisplay > maxX) |
mode->status = MODE_VIRTUAL_X; |
|
932,12 → 937,10 |
* @mode_list: list of modes to check |
* @verbose: be verbose about it |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* Once mode list generation is complete, a caller can use this routine to |
* remove invalid modes from a mode list. If any of the modes have a |
* status other than %MODE_OK, they are removed from @mode_list and freed. |
* This helper function can be used to prune a display mode list after |
* validation has been completed. All modes who's 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) |
964,13 → 967,10 |
* @lh_a: list_head for first mode |
* @lh_b: list_head for second mode |
* |
* LOCKING: |
* None. |
* |
* Compare two modes, given by @lh_a and @lh_b, returning a value indicating |
* which is better. |
* |
* RETURNS: |
* 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. |
*/ |
998,12 → 998,9 |
|
/** |
* drm_mode_sort - sort mode list |
* @mode_list: list to sort |
* @mode_list: list of drm_display_mode structures to sort |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* Sort @mode_list by favorability, putting good modes first. |
* Sort @mode_list by favorability, moving good modes to the head of the list. |
*/ |
void drm_mode_sort(struct list_head *mode_list) |
{ |
1014,21 → 1011,24 |
/** |
* drm_mode_connector_list_update - update the mode list for the connector |
* @connector: the connector to update |
* @merge_type_bits: whether to merge or overright type bits. |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* 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 modes. All modes unverified after this point |
* will be removed by the prune invalid modes. |
* 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_mode_connector_list_update(struct drm_connector *connector) |
void drm_mode_connector_list_update(struct drm_connector *connector, |
bool merge_type_bits) |
{ |
struct drm_display_mode *mode; |
struct drm_display_mode *pmode, *pt; |
int found_it; |
|
WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); |
|
list_for_each_entry_safe(pmode, pt, &connector->probed_modes, |
head) { |
found_it = 0; |
1039,7 → 1039,10 |
/* if equal delete the probed mode */ |
mode->status = pmode->status; |
/* Merge type bits together */ |
if (merge_type_bits) |
mode->type |= pmode->type; |
else |
mode->type = pmode->type; |
list_del(&pmode->head); |
drm_mode_destroy(connector->dev, pmode); |
break; |